From c0e1ef01f98a72d6ee21a0f759b198fcab0e4f8a Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 15 Feb 2022 15:28:37 -0800 Subject: [PATCH 001/242] Fix backwards count of Indic graphemes --- stdlib/public/core/StringGraphemeBreaking.swift | 3 ++- validation-test/stdlib/String.swift | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/StringGraphemeBreaking.swift b/stdlib/public/core/StringGraphemeBreaking.swift index b85d5b7aaefe2..db934a73dda89 100644 --- a/stdlib/public/core/StringGraphemeBreaking.swift +++ b/stdlib/public/core/StringGraphemeBreaking.swift @@ -390,7 +390,8 @@ extension _StringGuts { // If we're currently in an indic sequence (or if our lhs is a linking // consonant), then this check and everything underneath ensures that // we continue being in one and may check if this extend is a Virama. - if state.isInIndicSequence || scalar1._isLinkingConsonant { + if state.isInIndicSequence || + (!isBackwards && scalar1._isLinkingConsonant) { if y == .extend { let extendNormData = Unicode._NormData(scalar2, fastUpperbound: 0x300) diff --git a/validation-test/stdlib/String.swift b/validation-test/stdlib/String.swift index 9cb4c87adf006..f9c062bb59ceb 100644 --- a/validation-test/stdlib/String.swift +++ b/validation-test/stdlib/String.swift @@ -2333,6 +2333,13 @@ if #available(SwiftStdlib 5.6, *) { let test10 = "\u{003F}\u{094D}\u{0924}" // 2 expectEqual(2, test10.count) expectBidirectionalCount(2, test10) + +#if _runtime(_ObjC) + let test11Foreign = NSString(string: "\u{930}\u{93e}\u{91c}\u{94d}") // 2 + let test11 = test11Foreign as String + expectEqual(2, test11.count) + expectBidirectionalCount(2, test11) +#endif } } From 657c17fa39a284c545bced9c3ce09a348aaecb53 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 15 Feb 2022 17:16:36 -0800 Subject: [PATCH 002/242] Setup grapheme breaking tests --- .../StdlibUnicodeUnittest/CMakeLists.txt | 1 + .../GraphemeBreaking.swift | 70 ++ .../public/core/StringGraphemeBreaking.swift | 3 +- test/stdlib/Inputs/GraphemeBreakTest.txt | 784 ++++++++++++++++++ validation-test/stdlib/String.swift | 4 + .../stdlib/StringGraphemeBreaking.swift | 88 ++ 6 files changed, 949 insertions(+), 1 deletion(-) create mode 100644 stdlib/private/StdlibUnicodeUnittest/GraphemeBreaking.swift create mode 100644 test/stdlib/Inputs/GraphemeBreakTest.txt create mode 100644 validation-test/stdlib/StringGraphemeBreaking.swift diff --git a/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt b/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt index f5d52fa7222a1..16efc537c33e8 100644 --- a/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt +++ b/stdlib/private/StdlibUnicodeUnittest/CMakeLists.txt @@ -6,6 +6,7 @@ add_swift_target_library(swiftStdlibUnicodeUnittest ${SWIFT_STDLIB_LIBRARY_BUILD StdlibUnicodeUnittest.swift Collation.swift UnicodeScalarProperties.swift + GraphemeBreaking.swift SWIFT_MODULE_DEPENDS StdlibUnittest SWIFT_MODULE_DEPENDS_LINUX Glibc diff --git a/stdlib/private/StdlibUnicodeUnittest/GraphemeBreaking.swift b/stdlib/private/StdlibUnicodeUnittest/GraphemeBreaking.swift new file mode 100644 index 0000000000000..57074ce2576aa --- /dev/null +++ b/stdlib/private/StdlibUnicodeUnittest/GraphemeBreaking.swift @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// Normalization tests are currently only avaible on Darwin, awaiting a sensible +// file API... +#if _runtime(_ObjC) +import Foundation + +func parseGraphemeBreakTests( + _ data: String, + into result: inout [(String, Int)] +) { + for line in data.split(separator: "\n") { + // Only look at actual tests + guard line.hasPrefix("÷") else { + continue + } + + let info = line.split(separator: "#") + let components = info[0].split(separator: " ") + + var string = "" + var count = 0 + + for i in components.indices { + guard i != 0 else { + continue + } + + let scalar: Unicode.Scalar + + // If we're an odd index, this is a scalar. + if i & 0x1 == 1 { + scalar = Unicode.Scalar(UInt32(components[i], radix: 16)!)! + + string.unicodeScalars.append(scalar) + } else { + // Otherwise, it is a grapheme breaking operator. + + // If this is a break, record the +1 count. Otherwise it is × which is + // not a break. + if components[i] == "÷" { + count += 1 + } + } + } + + result.append((string, count)) + } +} + +public let graphemeBreakTests: [(String, Int)] = { + var result: [(String, Int)] = [] + + let testFile = readInputFile("GraphemeBreakTest.txt") + + parseGraphemeBreakTests(testFile, into: &result) + + return result +}() +#endif diff --git a/stdlib/public/core/StringGraphemeBreaking.swift b/stdlib/public/core/StringGraphemeBreaking.swift index db934a73dda89..2abfa59866597 100644 --- a/stdlib/public/core/StringGraphemeBreaking.swift +++ b/stdlib/public/core/StringGraphemeBreaking.swift @@ -441,7 +441,8 @@ extension _StringGuts { // GB999 default: // GB9c - if state.isInIndicSequence, state.hasSeenVirama, scalar2._isLinkingConsonant { + if !isBackwards, state.isInIndicSequence, state.hasSeenVirama, + scalar2._isLinkingConsonant { state.hasSeenVirama = false return false } diff --git a/test/stdlib/Inputs/GraphemeBreakTest.txt b/test/stdlib/Inputs/GraphemeBreakTest.txt new file mode 100644 index 0000000000000..4d7584574a708 --- /dev/null +++ b/test/stdlib/Inputs/GraphemeBreakTest.txt @@ -0,0 +1,784 @@ +# GraphemeBreakTest-cldr-14.0.0.txt +# Date: 2021-08-17, 04:43:19 GMT +# © 2021 Unicode®, Inc. +# Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries. +# For terms of use, see http://www.unicode.org/terms_of_use.html +# +# Unicode Character Database +# For documentation, see http://www.unicode.org/reports/tr44/ +# +# Default Grapheme_Cluster_Break Test +# +# Format: +# (# )? +# contains hex Unicode code points, with +# ÷ wherever there is a break opportunity, and +# × wherever there is not. +# the format can change, but currently it shows: +# - the sample character name +# - (x) the Grapheme_Cluster_Break property value for the sample character +# - [x] the rule that determines whether there is a break or not, +# as listed in the Rules section of GraphemeBreakTest.html +# +# These samples may be extended or changed in the future. +# +÷ 0020 ÷ 0020 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0020 × 0308 ÷ 0020 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0020 ÷ 000D ÷ # ÷ [0.2] SPACE (Other) ÷ [5.0] (CR) ÷ [0.3] +÷ 0020 × 0308 ÷ 000D ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 0020 ÷ 000A ÷ # ÷ [0.2] SPACE (Other) ÷ [5.0] (LF) ÷ [0.3] +÷ 0020 × 0308 ÷ 000A ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 0020 ÷ 0001 ÷ # ÷ [0.2] SPACE (Other) ÷ [5.0] (Control) ÷ [0.3] +÷ 0020 × 0308 ÷ 0001 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 0020 × 034F ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0020 × 0308 × 034F ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0020 ÷ 1F1E6 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0020 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0020 ÷ 0600 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0020 × 0308 ÷ 0600 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0020 × 0903 ÷ # ÷ [0.2] SPACE (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0020 × 0308 × 0903 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0020 ÷ 1100 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0020 × 0308 ÷ 1100 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0020 ÷ 1160 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0020 × 0308 ÷ 1160 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0020 ÷ 11A8 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0020 × 0308 ÷ 11A8 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0020 ÷ AC00 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0020 × 0308 ÷ AC00 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0020 ÷ AC01 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0020 × 0308 ÷ AC01 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0020 ÷ 0915 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0020 × 0308 ÷ 0915 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0020 ÷ 231A ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0020 × 0308 ÷ 231A ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0020 × 0300 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0020 × 0308 × 0300 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0020 × 094D ÷ # ÷ [0.2] SPACE (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0020 × 0308 × 094D ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0020 × 200D ÷ # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0020 × 0308 × 200D ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0020 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) ÷ [999.0] (Other) ÷ [0.3] +÷ 0020 × 0308 ÷ 0378 ÷ # ÷ [0.2] SPACE (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 000D ÷ 0020 ÷ # ÷ [0.2] (CR) ÷ [4.0] SPACE (Other) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 000D ÷ 000D ÷ # ÷ [0.2] (CR) ÷ [4.0] (CR) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 000D ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 000D × 000A ÷ # ÷ [0.2] (CR) × [3.0] (LF) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 000A ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 000D ÷ 0001 ÷ # ÷ [0.2] (CR) ÷ [4.0] (Control) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 0001 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 000D ÷ 034F ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 000D ÷ 0308 × 034F ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 000D ÷ 1F1E6 ÷ # ÷ [0.2] (CR) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 000D ÷ 0600 ÷ # ÷ [0.2] (CR) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 0600 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 000D ÷ 0903 ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 000D ÷ 0308 × 0903 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 000D ÷ 1100 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 000D ÷ 1160 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 000D ÷ 11A8 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 000D ÷ AC00 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 000D ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 000D ÷ AC01 ÷ # ÷ [0.2] (CR) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 000D ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 000D ÷ 0915 ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 0915 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 000D ÷ 231A ÷ # ÷ [0.2] (CR) ÷ [4.0] WATCH (ExtPict) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 231A ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 000D ÷ 0300 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 000D ÷ 0308 × 0300 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 000D ÷ 094D ÷ # ÷ [0.2] (CR) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 000D ÷ 0308 × 094D ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 000D ÷ 200D ÷ # ÷ [0.2] (CR) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 000D ÷ 0308 × 200D ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 000D ÷ 0378 ÷ # ÷ [0.2] (CR) ÷ [4.0] (Other) ÷ [0.3] +÷ 000D ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] (CR) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 000A ÷ 0020 ÷ # ÷ [0.2] (LF) ÷ [4.0] SPACE (Other) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 000A ÷ 000D ÷ # ÷ [0.2] (LF) ÷ [4.0] (CR) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 000D ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 000A ÷ 000A ÷ # ÷ [0.2] (LF) ÷ [4.0] (LF) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 000A ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 000A ÷ 0001 ÷ # ÷ [0.2] (LF) ÷ [4.0] (Control) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 0001 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 000A ÷ 034F ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 000A ÷ 0308 × 034F ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 000A ÷ 1F1E6 ÷ # ÷ [0.2] (LF) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 000A ÷ 0600 ÷ # ÷ [0.2] (LF) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 0600 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 000A ÷ 0903 ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 000A ÷ 0308 × 0903 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 000A ÷ 1100 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 000A ÷ 1160 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 000A ÷ 11A8 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 000A ÷ AC00 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 000A ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 000A ÷ AC01 ÷ # ÷ [0.2] (LF) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 000A ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 000A ÷ 0915 ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 0915 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 000A ÷ 231A ÷ # ÷ [0.2] (LF) ÷ [4.0] WATCH (ExtPict) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 231A ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 000A ÷ 0300 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 000A ÷ 0308 × 0300 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 000A ÷ 094D ÷ # ÷ [0.2] (LF) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 000A ÷ 0308 × 094D ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 000A ÷ 200D ÷ # ÷ [0.2] (LF) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 000A ÷ 0308 × 200D ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 000A ÷ 0378 ÷ # ÷ [0.2] (LF) ÷ [4.0] (Other) ÷ [0.3] +÷ 000A ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 0001 ÷ 0020 ÷ # ÷ [0.2] (Control) ÷ [4.0] SPACE (Other) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 0020 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0001 ÷ 000D ÷ # ÷ [0.2] (Control) ÷ [4.0] (CR) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 000D ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 0001 ÷ 000A ÷ # ÷ [0.2] (Control) ÷ [4.0] (LF) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 000A ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 0001 ÷ 0001 ÷ # ÷ [0.2] (Control) ÷ [4.0] (Control) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 0001 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 0001 ÷ 034F ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0001 ÷ 0308 × 034F ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0001 ÷ 1F1E6 ÷ # ÷ [0.2] (Control) ÷ [4.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0001 ÷ 0600 ÷ # ÷ [0.2] (Control) ÷ [4.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 0600 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0001 ÷ 0903 ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0001 ÷ 0308 × 0903 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0001 ÷ 1100 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 1100 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0001 ÷ 1160 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 1160 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0001 ÷ 11A8 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 11A8 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0001 ÷ AC00 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ AC00 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0001 ÷ AC01 ÷ # ÷ [0.2] (Control) ÷ [4.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ AC01 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0001 ÷ 0915 ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 0915 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0001 ÷ 231A ÷ # ÷ [0.2] (Control) ÷ [4.0] WATCH (ExtPict) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 231A ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0001 ÷ 0300 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0001 ÷ 0308 × 0300 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0001 ÷ 094D ÷ # ÷ [0.2] (Control) ÷ [4.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0001 ÷ 0308 × 094D ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0001 ÷ 200D ÷ # ÷ [0.2] (Control) ÷ [4.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0001 ÷ 0308 × 200D ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0001 ÷ 0378 ÷ # ÷ [0.2] (Control) ÷ [4.0] (Other) ÷ [0.3] +÷ 0001 ÷ 0308 ÷ 0378 ÷ # ÷ [0.2] (Control) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 034F ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 034F × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 034F ÷ 000D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (CR) ÷ [0.3] +÷ 034F × 0308 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 034F ÷ 000A ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (LF) ÷ [0.3] +÷ 034F × 0308 ÷ 000A ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 034F ÷ 0001 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [5.0] (Control) ÷ [0.3] +÷ 034F × 0308 ÷ 0001 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 034F × 034F ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 034F × 0308 × 034F ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 034F ÷ 1F1E6 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 034F × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 034F ÷ 0600 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 034F × 0308 ÷ 0600 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 034F × 0903 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 034F × 0308 × 0903 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 034F ÷ 1100 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 034F × 0308 ÷ 1100 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 034F ÷ 1160 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 034F × 0308 ÷ 1160 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 034F ÷ 11A8 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 034F × 0308 ÷ 11A8 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 034F ÷ AC00 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 034F × 0308 ÷ AC00 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 034F ÷ AC01 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 034F × 0308 ÷ AC01 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 034F ÷ 0915 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 034F × 0308 ÷ 0915 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 034F ÷ 231A ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 034F × 0308 ÷ 231A ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 034F × 0300 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 034F × 0308 × 0300 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 034F × 094D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 034F × 0308 × 094D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 034F × 200D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 034F × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 034F ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) ÷ [999.0] (Other) ÷ [0.3] +÷ 034F × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAPHEME JOINER (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 1F1E6 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 0020 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1F1E6 ÷ 000D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (CR) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 000D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 1F1E6 ÷ 000A ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (LF) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 000A ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 1F1E6 ÷ 0001 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [5.0] (Control) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 0001 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 1F1E6 × 034F ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 1F1E6 × 0308 × 034F ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 1F1E6 × 1F1E6 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 1F1E6 ÷ 0600 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 0600 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 1F1E6 × 0903 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1F1E6 × 0308 × 0903 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1F1E6 ÷ 1100 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 1100 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1F1E6 ÷ 1160 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 1160 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 1F1E6 ÷ 11A8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 11A8 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 1F1E6 ÷ AC00 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ AC00 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 1F1E6 ÷ AC01 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ AC01 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 1F1E6 ÷ 0915 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 0915 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1F1E6 ÷ 231A ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 231A ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 1F1E6 × 0300 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 1F1E6 × 0308 × 0300 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 1F1E6 × 094D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 1F1E6 × 0308 × 094D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 1F1E6 × 200D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 1F1E6 × 0308 × 200D ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 1F1E6 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [999.0] (Other) ÷ [0.3] +÷ 1F1E6 × 0308 ÷ 0378 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 0600 × 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] SPACE (Other) ÷ [0.3] +÷ 0600 × 0308 ÷ 0020 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0600 ÷ 000D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (CR) ÷ [0.3] +÷ 0600 × 0308 ÷ 000D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 0600 ÷ 000A ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (LF) ÷ [0.3] +÷ 0600 × 0308 ÷ 000A ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 0600 ÷ 0001 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) ÷ [5.0] (Control) ÷ [0.3] +÷ 0600 × 0308 ÷ 0001 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 0600 × 034F ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0600 × 0308 × 034F ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0600 × 1F1E6 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0600 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0600 × 0600 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0600 × 0308 ÷ 0600 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0600 × 0903 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0600 × 0308 × 0903 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0600 × 1100 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0600 × 0308 ÷ 1100 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0600 × 1160 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0600 × 0308 ÷ 1160 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0600 × 11A8 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0600 × 0308 ÷ 11A8 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0600 × AC00 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0600 × 0308 ÷ AC00 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0600 × AC01 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0600 × 0308 ÷ AC01 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0600 × 0915 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0600 × 0308 ÷ 0915 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0600 × 231A ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] WATCH (ExtPict) ÷ [0.3] +÷ 0600 × 0308 ÷ 231A ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0600 × 0300 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0600 × 0308 × 0300 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0600 × 094D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0600 × 0308 × 094D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0600 × 200D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0600 × 0308 × 200D ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0600 × 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.2] (Other) ÷ [0.3] +÷ 0600 × 0308 ÷ 0378 ÷ # ÷ [0.2] ARABIC NUMBER SIGN (Prepend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 0903 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0903 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0903 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (CR) ÷ [0.3] +÷ 0903 × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 0903 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (LF) ÷ [0.3] +÷ 0903 × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 0903 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [5.0] (Control) ÷ [0.3] +÷ 0903 × 0308 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 0903 × 034F ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0903 × 0308 × 034F ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0903 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0903 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0903 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0903 × 0308 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0903 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0903 × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0903 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0903 × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0903 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0903 × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0903 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0903 × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0903 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0903 × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0903 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0903 × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0903 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0903 × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0903 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0903 × 0308 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0903 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0903 × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0903 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0903 × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0903 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0903 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0903 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] (Other) ÷ [0.3] +÷ 0903 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VISARGA (SpacingMark) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 1100 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1100 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1100 ÷ 000D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (CR) ÷ [0.3] +÷ 1100 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 1100 ÷ 000A ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (LF) ÷ [0.3] +÷ 1100 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 1100 ÷ 0001 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [5.0] (Control) ÷ [0.3] +÷ 1100 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 1100 × 034F ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 1100 × 0308 × 034F ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 1100 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 1100 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 1100 ÷ 0600 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 1100 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 1100 × 0903 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1100 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1100 × 1100 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1100 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1100 × 1160 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 1100 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 1100 ÷ 11A8 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 1100 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 1100 × AC00 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 1100 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 1100 × AC01 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 1100 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 1100 ÷ 0915 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1100 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1100 ÷ 231A ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 1100 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 1100 × 0300 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 1100 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 1100 × 094D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 1100 × 0308 × 094D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 1100 × 200D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 1100 × 0308 × 200D ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 1100 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) ÷ [999.0] (Other) ÷ [0.3] +÷ 1100 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 1160 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1160 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1160 ÷ 000D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (CR) ÷ [0.3] +÷ 1160 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 1160 ÷ 000A ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (LF) ÷ [0.3] +÷ 1160 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 1160 ÷ 0001 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [5.0] (Control) ÷ [0.3] +÷ 1160 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 1160 × 034F ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 1160 × 0308 × 034F ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 1160 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 1160 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 1160 ÷ 0600 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 1160 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 1160 × 0903 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1160 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 1160 ÷ 1100 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1160 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1160 × 1160 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 1160 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 1160 × 11A8 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 1160 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 1160 ÷ AC00 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 1160 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 1160 ÷ AC01 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 1160 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 1160 ÷ 0915 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1160 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 1160 ÷ 231A ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 1160 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 1160 × 0300 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 1160 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 1160 × 094D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 1160 × 0308 × 094D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 1160 × 200D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 1160 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 1160 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) ÷ [999.0] (Other) ÷ [0.3] +÷ 1160 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JUNGSEONG FILLER (V) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 11A8 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 11A8 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 11A8 ÷ 000D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (CR) ÷ [0.3] +÷ 11A8 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 11A8 ÷ 000A ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (LF) ÷ [0.3] +÷ 11A8 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 11A8 ÷ 0001 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [5.0] (Control) ÷ [0.3] +÷ 11A8 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 11A8 × 034F ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 11A8 × 0308 × 034F ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 11A8 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 11A8 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 11A8 ÷ 0600 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 11A8 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 11A8 × 0903 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 11A8 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 11A8 ÷ 1100 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 11A8 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 11A8 ÷ 1160 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 11A8 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 11A8 × 11A8 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 11A8 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 11A8 ÷ AC00 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 11A8 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 11A8 ÷ AC01 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 11A8 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 11A8 ÷ 0915 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 11A8 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 11A8 ÷ 231A ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 11A8 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 11A8 × 0300 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 11A8 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 11A8 × 094D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 11A8 × 0308 × 094D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 11A8 × 200D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 11A8 × 0308 × 200D ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 11A8 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] (Other) ÷ [0.3] +÷ 11A8 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL JONGSEONG KIYEOK (T) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ AC00 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ AC00 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ AC00 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (CR) ÷ [0.3] +÷ AC00 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ AC00 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (LF) ÷ [0.3] +÷ AC00 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ AC00 ÷ 0001 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [5.0] (Control) ÷ [0.3] +÷ AC00 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ AC00 × 034F ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ AC00 × 0308 × 034F ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ AC00 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ AC00 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ AC00 ÷ 0600 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ AC00 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ AC00 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ AC00 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ AC00 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ AC00 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ AC00 × 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ AC00 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ AC00 × 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ AC00 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ AC00 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ AC00 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ AC00 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ AC00 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ AC00 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ AC00 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ AC00 ÷ 231A ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ AC00 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ AC00 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ AC00 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ AC00 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ AC00 × 0308 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ AC00 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ AC00 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ AC00 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) ÷ [999.0] (Other) ÷ [0.3] +÷ AC00 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ AC01 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ AC01 × 0308 ÷ 0020 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ AC01 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (CR) ÷ [0.3] +÷ AC01 × 0308 ÷ 000D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ AC01 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (LF) ÷ [0.3] +÷ AC01 × 0308 ÷ 000A ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ AC01 ÷ 0001 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [5.0] (Control) ÷ [0.3] +÷ AC01 × 0308 ÷ 0001 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ AC01 × 034F ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ AC01 × 0308 × 034F ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ AC01 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ AC01 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ AC01 ÷ 0600 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ AC01 × 0308 ÷ 0600 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ AC01 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ AC01 × 0308 × 0903 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ AC01 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ AC01 × 0308 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ AC01 ÷ 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ AC01 × 0308 ÷ 1160 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ AC01 × 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ AC01 × 0308 ÷ 11A8 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ AC01 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ AC01 × 0308 ÷ AC00 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ AC01 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ AC01 × 0308 ÷ AC01 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ AC01 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ AC01 × 0308 ÷ 0915 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ AC01 ÷ 231A ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ AC01 × 0308 ÷ 231A ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ AC01 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ AC01 × 0308 × 0300 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ AC01 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ AC01 × 0308 × 094D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ AC01 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ AC01 × 0308 × 200D ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ AC01 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) ÷ [999.0] (Other) ÷ [0.3] +÷ AC01 × 0308 ÷ 0378 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 0915 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0915 × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0915 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] (CR) ÷ [0.3] +÷ 0915 × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 0915 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] (LF) ÷ [0.3] +÷ 0915 × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 0915 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [5.0] (Control) ÷ [0.3] +÷ 0915 × 0308 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 0915 × 034F ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0915 × 0308 × 034F ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0915 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0915 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0915 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0915 × 0308 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0915 × 0903 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0915 × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0915 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0915 × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0915 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0915 × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0915 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0915 × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0915 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0915 × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0915 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0915 × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0915 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0915 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0915 × 0308 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0915 × 0300 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0915 × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0915 × 094D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0915 × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0915 × 200D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0915 × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0915 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] (Other) ÷ [0.3] +÷ 0915 × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 231A ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 231A × 0308 ÷ 0020 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 231A ÷ 000D ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (CR) ÷ [0.3] +÷ 231A × 0308 ÷ 000D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 231A ÷ 000A ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (LF) ÷ [0.3] +÷ 231A × 0308 ÷ 000A ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 231A ÷ 0001 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [5.0] (Control) ÷ [0.3] +÷ 231A × 0308 ÷ 0001 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 231A × 034F ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 231A × 0308 × 034F ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 231A ÷ 1F1E6 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 231A × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 231A ÷ 0600 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 231A × 0308 ÷ 0600 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 231A × 0903 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 231A × 0308 × 0903 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 231A ÷ 1100 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 231A × 0308 ÷ 1100 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 231A ÷ 1160 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 231A × 0308 ÷ 1160 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 231A ÷ 11A8 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 231A × 0308 ÷ 11A8 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 231A ÷ AC00 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 231A × 0308 ÷ AC00 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 231A ÷ AC01 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 231A × 0308 ÷ AC01 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 231A ÷ 0915 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 231A × 0308 ÷ 0915 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 231A ÷ 231A ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 231A × 0308 ÷ 231A ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 231A × 0300 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 231A × 0308 × 0300 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 231A × 094D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 231A × 0308 × 094D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 231A × 200D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 231A × 0308 × 200D ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 231A ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) ÷ [999.0] (Other) ÷ [0.3] +÷ 231A × 0308 ÷ 0378 ÷ # ÷ [0.2] WATCH (ExtPict) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 0300 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0300 × 0308 ÷ 0020 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0300 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 0300 × 0308 ÷ 000D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 0300 ÷ 000A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 0300 × 0308 ÷ 000A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 0300 ÷ 0001 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 0300 × 0308 ÷ 0001 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 0300 × 034F ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0300 × 0308 × 034F ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0300 ÷ 1F1E6 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0300 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0300 ÷ 0600 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0300 × 0308 ÷ 0600 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0300 × 0903 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0300 × 0308 × 0903 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0300 ÷ 1100 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0300 × 0308 ÷ 1100 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0300 ÷ 1160 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0300 × 0308 ÷ 1160 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0300 ÷ 11A8 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0300 × 0308 ÷ 11A8 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0300 ÷ AC00 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0300 × 0308 ÷ AC00 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0300 ÷ AC01 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0300 × 0308 ÷ AC01 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0300 ÷ 0915 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0300 × 0308 ÷ 0915 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0300 ÷ 231A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0300 × 0308 ÷ 231A ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0300 × 0300 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0300 × 0308 × 0300 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0300 × 094D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0300 × 0308 × 094D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0300 × 200D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0300 × 0308 × 200D ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0300 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 0300 × 0308 ÷ 0378 ÷ # ÷ [0.2] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 094D ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 094D × 0308 ÷ 0020 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 094D ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 094D × 0308 ÷ 000D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 094D ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 094D × 0308 ÷ 000A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 094D ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 094D × 0308 ÷ 0001 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 094D × 034F ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 094D × 0308 × 034F ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 094D ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 094D × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 094D ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 094D × 0308 ÷ 0600 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 094D × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 094D × 0308 × 0903 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 094D ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 094D × 0308 ÷ 1100 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 094D ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 094D × 0308 ÷ 1160 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 094D ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 094D × 0308 ÷ 11A8 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 094D ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 094D × 0308 ÷ AC00 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 094D ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 094D × 0308 ÷ AC01 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 094D ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 094D × 0308 ÷ 0915 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 094D ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 094D × 0308 ÷ 231A ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 094D × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 094D × 0308 × 0300 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 094D × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 094D × 0308 × 094D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 094D × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 094D × 0308 × 200D ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 094D ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 094D × 0308 ÷ 0378 ÷ # ÷ [0.2] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 200D ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 200D × 0308 ÷ 0020 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 200D ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 200D × 0308 ÷ 000D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 200D ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 200D × 0308 ÷ 000A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 200D ÷ 0001 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 200D × 0308 ÷ 0001 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 200D × 034F ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 200D × 0308 × 034F ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 200D ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 200D × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 200D ÷ 0600 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 200D × 0308 ÷ 0600 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 200D × 0903 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 200D × 0308 × 0903 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 200D ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 200D × 0308 ÷ 1100 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 200D ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 200D × 0308 ÷ 1160 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 200D ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 200D × 0308 ÷ 11A8 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 200D ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 200D × 0308 ÷ AC00 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 200D ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 200D × 0308 ÷ AC01 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 200D ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 200D × 0308 ÷ 0915 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 200D ÷ 231A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 200D × 0308 ÷ 231A ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 200D × 0300 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 200D × 0308 × 0300 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 200D × 094D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 200D × 0308 × 094D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 200D × 200D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 200D × 0308 × 200D ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 200D ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 200D × 0308 ÷ 0378 ÷ # ÷ [0.2] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 0378 ÷ 0020 ÷ # ÷ [0.2] (Other) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0378 × 0308 ÷ 0020 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 0378 ÷ 000D ÷ # ÷ [0.2] (Other) ÷ [5.0] (CR) ÷ [0.3] +÷ 0378 × 0308 ÷ 000D ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (CR) ÷ [0.3] +÷ 0378 ÷ 000A ÷ # ÷ [0.2] (Other) ÷ [5.0] (LF) ÷ [0.3] +÷ 0378 × 0308 ÷ 000A ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (LF) ÷ [0.3] +÷ 0378 ÷ 0001 ÷ # ÷ [0.2] (Other) ÷ [5.0] (Control) ÷ [0.3] +÷ 0378 × 0308 ÷ 0001 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [5.0] (Control) ÷ [0.3] +÷ 0378 × 034F ÷ # ÷ [0.2] (Other) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0378 × 0308 × 034F ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAPHEME JOINER (Extend) ÷ [0.3] +÷ 0378 ÷ 1F1E6 ÷ # ÷ [0.2] (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0378 × 0308 ÷ 1F1E6 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) ÷ [0.3] +÷ 0378 ÷ 0600 ÷ # ÷ [0.2] (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0378 × 0308 ÷ 0600 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) ÷ [0.3] +÷ 0378 × 0903 ÷ # ÷ [0.2] (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0378 × 0308 × 0903 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [0.3] +÷ 0378 ÷ 1100 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0378 × 0308 ÷ 1100 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 0378 ÷ 1160 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0378 × 0308 ÷ 1160 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JUNGSEONG FILLER (V) ÷ [0.3] +÷ 0378 ÷ 11A8 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0378 × 0308 ÷ 11A8 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL JONGSEONG KIYEOK (T) ÷ [0.3] +÷ 0378 ÷ AC00 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0378 × 0308 ÷ AC00 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GA (LV) ÷ [0.3] +÷ 0378 ÷ AC01 ÷ # ÷ [0.2] (Other) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0378 × 0308 ÷ AC01 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] HANGUL SYLLABLE GAG (LVT) ÷ [0.3] +÷ 0378 ÷ 0915 ÷ # ÷ [0.2] (Other) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0378 × 0308 ÷ 0915 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [0.3] +÷ 0378 ÷ 231A ÷ # ÷ [0.2] (Other) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0378 × 0308 ÷ 231A ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] WATCH (ExtPict) ÷ [0.3] +÷ 0378 × 0300 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0378 × 0308 × 0300 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] COMBINING GRAVE ACCENT (Extend_ExtCccZwj) ÷ [0.3] +÷ 0378 × 094D ÷ # ÷ [0.2] (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0378 × 0308 × 094D ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [0.3] +÷ 0378 × 200D ÷ # ÷ [0.2] (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0378 × 0308 × 200D ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0378 ÷ 0378 ÷ # ÷ [0.2] (Other) ÷ [999.0] (Other) ÷ [0.3] +÷ 0378 × 0308 ÷ 0378 ÷ # ÷ [0.2] (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] (Other) ÷ [0.3] +÷ 000D × 000A ÷ 0061 ÷ 000A ÷ 0308 ÷ # ÷ [0.2] (CR) × [3.0] (LF) ÷ [4.0] LATIN SMALL LETTER A (Other) ÷ [5.0] (LF) ÷ [4.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3] +÷ 0061 × 0308 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [0.3] +÷ 0020 × 200D ÷ 0646 ÷ # ÷ [0.2] SPACE (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] ARABIC LETTER NOON (Other) ÷ [0.3] +÷ 0646 × 200D ÷ 0020 ÷ # ÷ [0.2] ARABIC LETTER NOON (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] SPACE (Other) ÷ [0.3] +÷ 1100 × 1100 ÷ # ÷ [0.2] HANGUL CHOSEONG KIYEOK (L) × [6.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ AC00 × 11A8 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GA (LV) × [7.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ AC01 × 11A8 ÷ 1100 ÷ # ÷ [0.2] HANGUL SYLLABLE GAG (LVT) × [8.0] HANGUL JONGSEONG KIYEOK (T) ÷ [999.0] HANGUL CHOSEONG KIYEOK (L) ÷ [0.3] +÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷ # ÷ [0.2] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [12.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] +÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] +÷ 0061 ÷ 1F1E6 × 1F1E7 × 200D ÷ 1F1E8 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] +÷ 0061 ÷ 1F1E6 × 200D ÷ 1F1E7 × 1F1E8 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] +÷ 0061 ÷ 1F1E6 × 1F1E7 ÷ 1F1E8 × 1F1E9 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER A (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER B (RI) ÷ [999.0] REGIONAL INDICATOR SYMBOL LETTER C (RI) × [13.0] REGIONAL INDICATOR SYMBOL LETTER D (RI) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] +÷ 0061 × 200D ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [0.3] +÷ 0061 × 0308 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] +÷ 0061 × 0903 ÷ 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.1] DEVANAGARI SIGN VISARGA (SpacingMark) ÷ [999.0] LATIN SMALL LETTER B (Other) ÷ [0.3] +÷ 0061 ÷ 0600 × 0062 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) ÷ [999.0] ARABIC NUMBER SIGN (Prepend) × [9.2] LATIN SMALL LETTER B (Other) ÷ [0.3] +÷ 1F476 × 1F3FF ÷ 1F476 ÷ # ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) ÷ [999.0] BABY (ExtPict) ÷ [0.3] +÷ 0061 × 1F3FF ÷ 1F476 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) ÷ [999.0] BABY (ExtPict) ÷ [0.3] +÷ 0061 × 1F3FF ÷ 1F476 × 200D × 1F6D1 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) ÷ [999.0] BABY (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3] +÷ 1F476 × 1F3FF × 0308 × 200D × 1F476 × 1F3FF ÷ # ÷ [0.2] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) × [9.0] COMBINING DIAERESIS (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] BABY (ExtPict) × [9.0] EMOJI MODIFIER FITZPATRICK TYPE-6 (Extend) ÷ [0.3] +÷ 1F6D1 × 200D × 1F6D1 ÷ # ÷ [0.2] OCTAGONAL SIGN (ExtPict) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3] +÷ 0061 × 200D ÷ 1F6D1 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] OCTAGONAL SIGN (ExtPict) ÷ [0.3] +÷ 2701 × 200D × 2701 ÷ # ÷ [0.2] UPPER BLADE SCISSORS (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [11.0] UPPER BLADE SCISSORS (Other) ÷ [0.3] +÷ 0061 × 200D ÷ 2701 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) ÷ [999.0] UPPER BLADE SCISSORS (Other) ÷ [0.3] +÷ 0915 ÷ 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D × 200D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 093C × 200D × 094D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 093C × 094D × 200D × 0924 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN NUKTA (Extend_ExtCccZwj) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.0] ZERO WIDTH JOINER (ZWJ_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D × 0924 × 094D × 092F ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.3] DEVANAGARI LETTER TA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) × [9.3] DEVANAGARI LETTER YA (LinkingConsonant) ÷ [0.3] +÷ 0915 × 094D ÷ 0061 ÷ # ÷ [0.2] DEVANAGARI LETTER KA (LinkingConsonant) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] LATIN SMALL LETTER A (Other) ÷ [0.3] +÷ 0061 × 094D ÷ 0924 ÷ # ÷ [0.2] LATIN SMALL LETTER A (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +÷ 003F × 094D ÷ 0924 ÷ # ÷ [0.2] QUESTION MARK (Other) × [9.0] DEVANAGARI SIGN VIRAMA (Extend_Virama_ExtCccZwj) ÷ [999.0] DEVANAGARI LETTER TA (LinkingConsonant) ÷ [0.3] +# +# Lines: 756 +# +# EOF diff --git a/validation-test/stdlib/String.swift b/validation-test/stdlib/String.swift index f9c062bb59ceb..8c89817b6e99f 100644 --- a/validation-test/stdlib/String.swift +++ b/validation-test/stdlib/String.swift @@ -2340,6 +2340,10 @@ if #available(SwiftStdlib 5.6, *) { expectEqual(2, test11.count) expectBidirectionalCount(2, test11) #endif + + let test12 = "a\u{0915}\u{093C}\u{200D}\u{094D}\u{0924}a" // 3 + expectEqual(3, test12.count) + expectBidirectionalCount(3, test12) } } diff --git a/validation-test/stdlib/StringGraphemeBreaking.swift b/validation-test/stdlib/StringGraphemeBreaking.swift new file mode 100644 index 0000000000000..650e772292427 --- /dev/null +++ b/validation-test/stdlib/StringGraphemeBreaking.swift @@ -0,0 +1,88 @@ +// RUN: %empty-directory(%t) +// RUN: %target-run-stdlib-swift %S/Inputs/ + +// REQUIRES: executable_test +// REQUIRES: objc_interop +// REQUIRES: optimized_stdlib + +import StdlibUnittest +import StdlibUnicodeUnittest +import Foundation + +let StringGraphemeBreaking = TestSuite("StringGraphemeBreaking") + +if #available(SwiftStdlib 5.6, *) { + StringGraphemeBreaking.test("grapheme breaking") { + for graphemeBreakTest in graphemeBreakTests { + expectEqual(graphemeBreakTest.1, graphemeBreakTest.0.count) + } + } +} + +// The most simple subclass of NSString that CoreFoundation does not know +// about. +class NonContiguousNSString : NSString { + required init(coder aDecoder: NSCoder) { + fatalError("don't call this initializer") + } + required init(itemProviderData data: Data, typeIdentifier: String) throws { + fatalError("don't call this initializer") + } + + override init() { + _value = [] + super.init() + } + + init(_ value: [UInt16]) { + _value = value + super.init() + } + + @objc(copyWithZone:) override func copy(with zone: NSZone?) -> Any { + // Ensure that copying this string produces a class that CoreFoundation + // does not know about. + return self + } + + @objc override var length: Int { + return _value.count + } + + @objc override func character(at index: Int) -> unichar { + return _value[index] + } + + var _value: [UInt16] +} + +extension _StringGuts { + @_silgen_name("$ss11_StringGutsV9isForeignSbvg") + func _isForeign() -> Bool +} + +func getUTF16Array(from string: String) -> [UInt16] { + var result: [UInt16] = [] + + for cp in string.utf16 { + result.append(cp) + } + + return result +} + +if #available(SwiftStdlib 5.6, *) { + StringGraphemeBreaking.test("grapheme breaking foreign") { + for graphemeBreakTest in graphemeBreakTests { + let foreignTest = NonContiguousNSString( + getUTF16Array(from: graphemeBreakTest.0) + ) + let test = foreignTest as String + + expectTrue(test._guts._isForeign()) + expectEqual(graphemeBreakTest.1, test.count) + } + } +} + +runAllTests() From ec0750c5dc025006f0aa03bc3bb7f290e6f2001a Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Fri, 18 Feb 2022 13:35:22 -0800 Subject: [PATCH 003/242] [SILGen] Optimize generated dealloc for linearly recursive data structures Adds detection of linearly recursive data structures by finding stored properties that share the type of the class the dealloc is being generated for. Each link will then be deallocated in a loop, while ensuring to keep the next link alive to prevent the recursion. This prevents stack overflows for long chains while also improving performance. rdar://89162954 --- lib/SILGen/SILGenDestructor.cpp | 148 +++++++++++++++++++++++++++++++- lib/SILGen/SILGenFunction.h | 12 +++ 2 files changed, 157 insertions(+), 3 deletions(-) diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 1f78db4546d65..d1a21d2e04e64 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -14,6 +14,7 @@ #include "SILGenFunctionBuilder.h" #include "RValue.h" #include "ArgumentScope.h" +#include "llvm/ADT/SmallSet.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/SubstitutionMap.h" #include "swift/SIL/TypeLowering.h" @@ -219,6 +220,140 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc, } } +llvm::SmallSetVector findRecursiveLinks(DeclContext* DC, ClassDecl *cd) { + auto SelfTy = DC->mapTypeIntoContext(cd->getDeclaredType()); + + // Collect all stored properties that would form a recursive structure, + // so we can remove the recursion and prevent the call stack from + // overflowing. + llvm::SmallSetVector recursiveLinks; + for (VarDecl *vd : cd->getStoredProperties()) { + auto Ty = vd->getType()->getOptionalObjectType(); + if (Ty && Ty->getCanonicalType() == SelfTy->getCanonicalType()) { + recursiveLinks.insert(vd); + } + } + + // NOTE: Right now we only optimize linear recursion, so if there is more than one link, + // clear out the set and don't perform any recursion optimization. + if (recursiveLinks.size() < 1) { + recursiveLinks.clear(); + } + + return recursiveLinks; +} + + +void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, + ClassDecl *cd, + SmallSetVector recursiveLinks, + CleanupLocation cleanupLoc) { + auto SelfTy = F.mapTypeIntoContext(cd->getDeclaredType()); + + assert(recursiveLinks.size() <= 1 && "Only linear recursion supported."); + + auto SelfTyLowered = getTypeLowering(SelfTy).getLoweredType(); + for (VarDecl* vd : recursiveLinks) { + SILBasicBlock* cleanBB = createBasicBlock(); + SILBasicBlock* noneBB = createBasicBlock(); + SILBasicBlock* notUniqueBB = createBasicBlock(); + SILBasicBlock* uniqueBB = createBasicBlock(); + SILBasicBlock* someBB = createBasicBlock(); + SILBasicBlock* loopBB = createBasicBlock(); + + // var iter = self.link + // self.link = nil + auto Ty = getTypeLowering(vd->getType()).getLoweredType(); + auto optionalNone = B.createOptionalNone(cleanupLoc, Ty); + SILValue varAddr = + B.createRefElementAddr(cleanupLoc, selfValue.getValue(), vd, + Ty.getAddressType()); + auto iterAddr = B.createAllocStack(cleanupLoc, Ty); + SILValue addr = B.createBeginAccess( + cleanupLoc, varAddr, SILAccessKind::Modify, SILAccessEnforcement::Static, + false /*noNestedConflict*/, false /*fromBuiltin*/); + SILValue iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); + B.createStore(cleanupLoc, optionalNone, addr, StoreOwnershipQualifier::Assign); + B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); + B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Init); + + B.createBranch(cleanupLoc, loopBB); + + // while iter != nil { + B.emitBlock(loopBB); + SILValue operand = B.createLoad(cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy); + auto operandCopy = B.createCopyValue(cleanupLoc, operand); + auto operandAddr = B.createAllocStack(cleanupLoc, Ty); + B.createStore(cleanupLoc, operandCopy, operandAddr, StoreOwnershipQualifier::Init); + B.createDestroyValue(cleanupLoc, operand); + B.createSwitchEnumAddr( + cleanupLoc, operandAddr, nullptr, + {{getASTContext().getOptionalSomeDecl(), someBB}, + {std::make_pair(getASTContext().getOptionalNoneDecl(), noneBB)}}); + + // if isKnownUniquelyReferenced(&iter) { + B.emitBlock(someBB); + B.createDestroyAddr(cleanupLoc, operandAddr); + B.createDeallocStack(cleanupLoc, operandAddr); + auto isUnique = B.createIsUnique(cleanupLoc, iterAddr); + B.createCondBranch(cleanupLoc, isUnique, uniqueBB, notUniqueBB); + + + // we have a uniquely referenced link, so we need to deinit + B.emitBlock(uniqueBB); + + // NOTE: We increment the ref count of the tail instead of unlinking it, + // because custom deinit implementations of subclasses may access + // it and it would be semantically wrong to unset it before that. + // Making the tail non-uniquely referenced prevents the recursion. + + // let tail = iter.unsafelyUnwrapped.next + // iter = tail + SILValue _iter = B.createLoad(cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy); + auto iterBorrow = B.createBeginBorrow(cleanupLoc, _iter); + auto iterBorrowAddr = B.createAllocStack(cleanupLoc, Ty); + B.createStoreBorrow(cleanupLoc, iterBorrow, iterBorrowAddr); + auto xx = B.createLoadBorrow(cleanupLoc, iterBorrowAddr); + auto* link = B.createUncheckedEnumData(cleanupLoc, + xx, + getASTContext().getOptionalSomeDecl(), + SelfTyLowered); + varAddr = B.createRefElementAddr(cleanupLoc, + link, + vd, + Ty.getAddressType()); + + addr = B.createBeginAccess( + cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static, + false /* noNestedConflict */, false /*fromBuiltin*/); + iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); + B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); + B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign); + + B.createEndBorrow(cleanupLoc, xx); + B.createEndBorrow(cleanupLoc, iterBorrow); + + B.createDestroyValue(cleanupLoc, _iter); + B.createDeallocStack(cleanupLoc, iterBorrowAddr); + + B.createBranch(cleanupLoc, loopBB); + + // the next link in the chain is not unique, so we are done here + B.emitBlock(notUniqueBB); + B.createBranch(cleanupLoc, cleanBB); + + // we reached the end of the chain + B.emitBlock(noneBB); + B.createDeallocStack(cleanupLoc, operandAddr); + B.createBranch(cleanupLoc, cleanBB); + + + B.emitBlock(cleanBB); + B.createDestroyAddr(cleanupLoc, iterAddr); + B.createDeallocStack(cleanupLoc, iterAddr); + } +} + void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, ClassDecl *cd, CleanupLocation cleanupLoc) { @@ -239,21 +374,28 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, /// A distributed actor may be 'remote' in which case there is no need to /// destroy "all" members, because they never had storage to begin with. if (cd->isDistributedActor()) { - finishBB = createBasicBlock(); normalMemberDestroyBB = createBasicBlock(); - + finishBB = createBasicBlock(); emitDistributedActorClassMemberDestruction(cleanupLoc, selfValue, cd, normalMemberDestroyBB, finishBB); } + auto recursiveLinks = findRecursiveLinks(F.getDeclContext(), cd); + /// Destroy all members. { if (normalMemberDestroyBB) B.emitBlock(normalMemberDestroyBB); - for (VarDecl *vd : cd->getStoredProperties()) + for (VarDecl *vd : cd->getStoredProperties()) { + if (recursiveLinks.contains(vd)) + continue; destroyClassMember(cleanupLoc, selfValue, vd); + } + + if (!recursiveLinks.empty()) + emitRecursiveChainDestruction(selfValue, cd, recursiveLinks, cleanupLoc); if (finishBB) B.createBranch(cleanupLoc, finishBB); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 644ce60a55983..b78011b00cb35 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -680,6 +680,18 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction void emitClassMemberDestruction(ManagedValue selfValue, ClassDecl *cd, CleanupLocation cleanupLoc); + /// Generates code to destroy recursive data structures, without building + /// up the call stack. + /// + /// \param selfValue The 'self' value. + /// \param cd The class declaration whose members are being destroyed. + /// \param recursiveLinks The set of stored properties that form the + /// recursive data structure. + void emitRecursiveChainDestruction(ManagedValue selfValue, + ClassDecl *cd, + llvm::SmallSetVector recursiveLinks, + CleanupLocation cleanupLoc); + /// Generates a thunk from a foreign function to the native Swift convention. void emitForeignToNativeThunk(SILDeclRef thunk); /// Generates a thunk from a native function to foreign conventions. From 014fd2bb94753c6bdd6a738ff1c6579cdca15173 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Fri, 18 Feb 2022 16:03:29 -0800 Subject: [PATCH 004/242] Small cleanup --- lib/SILGen/SILGenDestructor.cpp | 96 ++++++++++++++++----------------- lib/SILGen/SILGenFunction.h | 8 +-- 2 files changed, 49 insertions(+), 55 deletions(-) diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index d1a21d2e04e64..7ac7b93d1afa9 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -10,14 +10,14 @@ // //===----------------------------------------------------------------------===// +#include "ArgumentScope.h" +#include "RValue.h" #include "SILGenFunction.h" #include "SILGenFunctionBuilder.h" -#include "RValue.h" -#include "ArgumentScope.h" -#include "llvm/ADT/SmallSet.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/SubstitutionMap.h" #include "swift/SIL/TypeLowering.h" +#include "llvm/ADT/SmallSet.h" using namespace swift; using namespace Lowering; @@ -220,13 +220,14 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc, } } -llvm::SmallSetVector findRecursiveLinks(DeclContext* DC, ClassDecl *cd) { +llvm::SmallSetVector findRecursiveLinks(DeclContext *DC, + ClassDecl *cd) { auto SelfTy = DC->mapTypeIntoContext(cd->getDeclaredType()); // Collect all stored properties that would form a recursive structure, // so we can remove the recursion and prevent the call stack from // overflowing. - llvm::SmallSetVector recursiveLinks; + llvm::SmallSetVector recursiveLinks; for (VarDecl *vd : cd->getStoredProperties()) { auto Ty = vd->getType()->getOptionalObjectType(); if (Ty && Ty->getCanonicalType() == SelfTy->getCanonicalType()) { @@ -234,8 +235,8 @@ llvm::SmallSetVector findRecursiveLinks(DeclContext* DC, ClassDecl } } - // NOTE: Right now we only optimize linear recursion, so if there is more than one link, - // clear out the set and don't perform any recursion optimization. + // NOTE: Right now we only optimize linear recursion, so if there is more than + // one link, clear out the set and don't perform any recursion optimization. if (recursiveLinks.size() < 1) { recursiveLinks.clear(); } @@ -243,37 +244,37 @@ llvm::SmallSetVector findRecursiveLinks(DeclContext* DC, ClassDecl return recursiveLinks; } - -void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, - ClassDecl *cd, - SmallSetVector recursiveLinks, - CleanupLocation cleanupLoc) { +void SILGenFunction::emitRecursiveChainDestruction( + ManagedValue selfValue, ClassDecl *cd, + SmallSetVector recursiveLinks, CleanupLocation cleanupLoc) { auto SelfTy = F.mapTypeIntoContext(cd->getDeclaredType()); assert(recursiveLinks.size() <= 1 && "Only linear recursion supported."); auto SelfTyLowered = getTypeLowering(SelfTy).getLoweredType(); - for (VarDecl* vd : recursiveLinks) { - SILBasicBlock* cleanBB = createBasicBlock(); - SILBasicBlock* noneBB = createBasicBlock(); - SILBasicBlock* notUniqueBB = createBasicBlock(); - SILBasicBlock* uniqueBB = createBasicBlock(); - SILBasicBlock* someBB = createBasicBlock(); - SILBasicBlock* loopBB = createBasicBlock(); + for (VarDecl *vd : recursiveLinks) { + SILBasicBlock *cleanBB = createBasicBlock(); + SILBasicBlock *noneBB = createBasicBlock(); + SILBasicBlock *notUniqueBB = createBasicBlock(); + SILBasicBlock *uniqueBB = createBasicBlock(); + SILBasicBlock *someBB = createBasicBlock(); + SILBasicBlock *loopBB = createBasicBlock(); // var iter = self.link // self.link = nil auto Ty = getTypeLowering(vd->getType()).getLoweredType(); auto optionalNone = B.createOptionalNone(cleanupLoc, Ty); - SILValue varAddr = - B.createRefElementAddr(cleanupLoc, selfValue.getValue(), vd, - Ty.getAddressType()); + SILValue varAddr = B.createRefElementAddr(cleanupLoc, selfValue.getValue(), + vd, Ty.getAddressType()); auto iterAddr = B.createAllocStack(cleanupLoc, Ty); - SILValue addr = B.createBeginAccess( - cleanupLoc, varAddr, SILAccessKind::Modify, SILAccessEnforcement::Static, - false /*noNestedConflict*/, false /*fromBuiltin*/); - SILValue iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); - B.createStore(cleanupLoc, optionalNone, addr, StoreOwnershipQualifier::Assign); + SILValue addr = + B.createBeginAccess(cleanupLoc, varAddr, SILAccessKind::Modify, + SILAccessEnforcement::Static, + false /*noNestedConflict*/, false /*fromBuiltin*/); + SILValue iter = + B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); + B.createStore(cleanupLoc, optionalNone, addr, + StoreOwnershipQualifier::Assign); B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Init); @@ -281,15 +282,17 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, // while iter != nil { B.emitBlock(loopBB); - SILValue operand = B.createLoad(cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy); + SILValue operand = + B.createLoad(cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy); auto operandCopy = B.createCopyValue(cleanupLoc, operand); auto operandAddr = B.createAllocStack(cleanupLoc, Ty); - B.createStore(cleanupLoc, operandCopy, operandAddr, StoreOwnershipQualifier::Init); + B.createStore(cleanupLoc, operandCopy, operandAddr, + StoreOwnershipQualifier::Init); B.createDestroyValue(cleanupLoc, operand); B.createSwitchEnumAddr( - cleanupLoc, operandAddr, nullptr, - {{getASTContext().getOptionalSomeDecl(), someBB}, - {std::make_pair(getASTContext().getOptionalNoneDecl(), noneBB)}}); + cleanupLoc, operandAddr, nullptr, + {{getASTContext().getOptionalSomeDecl(), someBB}, + {std::make_pair(getASTContext().getOptionalNoneDecl(), noneBB)}}); // if isKnownUniquelyReferenced(&iter) { B.emitBlock(someBB); @@ -298,7 +301,6 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, auto isUnique = B.createIsUnique(cleanupLoc, iterAddr); B.createCondBranch(cleanupLoc, isUnique, uniqueBB, notUniqueBB); - // we have a uniquely referenced link, so we need to deinit B.emitBlock(uniqueBB); @@ -309,32 +311,24 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, // let tail = iter.unsafelyUnwrapped.next // iter = tail - SILValue _iter = B.createLoad(cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy); + SILValue _iter = + B.createLoad(cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy); auto iterBorrow = B.createBeginBorrow(cleanupLoc, _iter); - auto iterBorrowAddr = B.createAllocStack(cleanupLoc, Ty); - B.createStoreBorrow(cleanupLoc, iterBorrow, iterBorrowAddr); - auto xx = B.createLoadBorrow(cleanupLoc, iterBorrowAddr); - auto* link = B.createUncheckedEnumData(cleanupLoc, - xx, - getASTContext().getOptionalSomeDecl(), - SelfTyLowered); - varAddr = B.createRefElementAddr(cleanupLoc, - link, - vd, - Ty.getAddressType()); + auto *link = B.createUncheckedEnumData( + cleanupLoc, iterBorrow, getASTContext().getOptionalSomeDecl(), + SelfTyLowered); + varAddr = B.createRefElementAddr(cleanupLoc, link, vd, Ty.getAddressType()); addr = B.createBeginAccess( - cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static, - false /* noNestedConflict */, false /*fromBuiltin*/); + cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static, + false /* noNestedConflict */, false /*fromBuiltin*/); iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign); - B.createEndBorrow(cleanupLoc, xx); B.createEndBorrow(cleanupLoc, iterBorrow); B.createDestroyValue(cleanupLoc, _iter); - B.createDeallocStack(cleanupLoc, iterBorrowAddr); B.createBranch(cleanupLoc, loopBB); @@ -347,7 +341,6 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, B.createDeallocStack(cleanupLoc, operandAddr); B.createBranch(cleanupLoc, cleanBB); - B.emitBlock(cleanBB); B.createDestroyAddr(cleanupLoc, iterAddr); B.createDeallocStack(cleanupLoc, iterAddr); @@ -374,8 +367,9 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, /// A distributed actor may be 'remote' in which case there is no need to /// destroy "all" members, because they never had storage to begin with. if (cd->isDistributedActor()) { - normalMemberDestroyBB = createBasicBlock(); finishBB = createBasicBlock(); + normalMemberDestroyBB = createBasicBlock(); + emitDistributedActorClassMemberDestruction(cleanupLoc, selfValue, cd, normalMemberDestroyBB, finishBB); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index b78011b00cb35..cc45eb4a2cfe9 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -687,10 +687,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// \param cd The class declaration whose members are being destroyed. /// \param recursiveLinks The set of stored properties that form the /// recursive data structure. - void emitRecursiveChainDestruction(ManagedValue selfValue, - ClassDecl *cd, - llvm::SmallSetVector recursiveLinks, - CleanupLocation cleanupLoc); + void emitRecursiveChainDestruction( + ManagedValue selfValue, ClassDecl *cd, + llvm::SmallSetVector recursiveLinks, + CleanupLocation cleanupLoc); /// Generates a thunk from a foreign function to the native Swift convention. void emitForeignToNativeThunk(SILDeclRef thunk); From d61e3863e4aeb9891903604f2dcf1b2ea6dd991c Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Sat, 19 Feb 2022 12:27:16 -0800 Subject: [PATCH 005/242] Add tests and incorporate feedback --- lib/SILGen/SILGenDestructor.cpp | 10 +- test/SILGen/deinit_recursive.swift | 91 +++++++++++++++++++ .../SILGen/deinit_recursive_no_overflow.swift | 23 +++++ 3 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 test/SILGen/deinit_recursive.swift create mode 100644 test/SILGen/deinit_recursive_no_overflow.swift diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 7ac7b93d1afa9..b901faa0ec0aa 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -222,14 +222,14 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc, llvm::SmallSetVector findRecursiveLinks(DeclContext *DC, ClassDecl *cd) { - auto SelfTy = DC->mapTypeIntoContext(cd->getDeclaredType()); + auto SelfTy = DC->mapTypeIntoContext(cd->getDeclaredInterfaceType()); // Collect all stored properties that would form a recursive structure, // so we can remove the recursion and prevent the call stack from // overflowing. llvm::SmallSetVector recursiveLinks; for (VarDecl *vd : cd->getStoredProperties()) { - auto Ty = vd->getType()->getOptionalObjectType(); + auto Ty = vd->getInterfaceType()->getOptionalObjectType(); if (Ty && Ty->getCanonicalType() == SelfTy->getCanonicalType()) { recursiveLinks.insert(vd); } @@ -237,7 +237,7 @@ llvm::SmallSetVector findRecursiveLinks(DeclContext *DC, // NOTE: Right now we only optimize linear recursion, so if there is more than // one link, clear out the set and don't perform any recursion optimization. - if (recursiveLinks.size() < 1) { + if (recursiveLinks.size() > 1) { recursiveLinks.clear(); } @@ -247,7 +247,7 @@ llvm::SmallSetVector findRecursiveLinks(DeclContext *DC, void SILGenFunction::emitRecursiveChainDestruction( ManagedValue selfValue, ClassDecl *cd, SmallSetVector recursiveLinks, CleanupLocation cleanupLoc) { - auto SelfTy = F.mapTypeIntoContext(cd->getDeclaredType()); + auto SelfTy = F.mapTypeIntoContext(cd->getDeclaredInterfaceType()); assert(recursiveLinks.size() <= 1 && "Only linear recursion supported."); @@ -262,7 +262,7 @@ void SILGenFunction::emitRecursiveChainDestruction( // var iter = self.link // self.link = nil - auto Ty = getTypeLowering(vd->getType()).getLoweredType(); + auto Ty = getTypeLowering(vd->getInterfaceType()).getLoweredType(); auto optionalNone = B.createOptionalNone(cleanupLoc, Ty); SILValue varAddr = B.createRefElementAddr(cleanupLoc, selfValue.getValue(), vd, Ty.getAddressType()); diff --git a/test/SILGen/deinit_recursive.swift b/test/SILGen/deinit_recursive.swift new file mode 100644 index 0000000000000..eafca443ad12e --- /dev/null +++ b/test/SILGen/deinit_recursive.swift @@ -0,0 +1,91 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +class Node { + var elem: Int64 + var next: Node? +} + +// CHECK: sil hidden [ossa] @$s16deinit_recursive4NodeCfd : $@convention(method) (@guaranteed Node) -> @owned Builtin.NativeObject { +// CHECK: [[SELF:%.*]] "self" +// CHECK: bb0([[SELF]] : @guaranteed $Node): +// CHECK: [[ELEM:%.*]] = ref_element_addr [[SELF]] : $Node, #Node.elem +// CHECK: [[ELEM_ACCESS:%.*]] = begin_access [deinit] [static] [[ELEM]] : $*Int64 +// CHECK: destroy_addr [[ELEM_ACCESS]] : $*Int64 +// CHECK: end_access [[ELEM_ACCESS]] : $*Int64 +// CHECK: [[NIL:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK: [[SELF_NEXT:%.*]] = ref_element_addr [[SELF]] : $Node, #Node.next +// CHECK: [[ITER:%.*]] = alloc_stack $Optional +// CHECK: [[SELF_NEXT_ACCESS:%.*]] = begin_access [modify] [static] [[SELF_NEXT]] : $*Optional +// CHECK: [[SELF_NEXT_COPY:%.*]] = load [copy] [[SELF_NEXT_ACCESS]] : $*Optional +// CHECK: store [[NIL]] to [assign] [[SELF_NEXT_ACCESS]] : $*Optional +// CHECK: end_access [[SELF_NEXT_ACCESS]] : $*Optional +// CHECK: store [[SELF_NEXT_COPY]] to [init] [[ITER]] : $*Optional +// CHECK: br [[LOOPBB:bb.*]] // + +// CHECK: [[LOOPBB]]: +// CHECK: [[ITER_ADDR:%.*]] = load [copy] [[ITER]] : $*Optional +// CHECK: [[ITER_COPY:%.*]] = copy_value [[ITER_ADDR]] : $Optional +// CHECK: [[ITER_COPY_ADDR:%.*]] = alloc_stack $Optional +// CHECK: store [[ITER_COPY]] to [init] [[ITER_COPY_ADDR]] : $*Optional +// CHECK: destroy_value [[ITER_ADDR]] : $Optional +// CHECK: switch_enum_addr [[ITER_COPY_ADDR]] : $*Optional, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] + +// CHECK: [[IS_SOME_BB]]: +// CHECK: destroy_addr [[ITER_COPY_ADDR]] : $*Optional +// CHECK: dealloc_stack [[ITER_COPY_ADDR]] : $*Optional +// CHECK: [[IS_UNIQUE:%.*]] = is_unique [[ITER]] : $*Optional +// CHECK: cond_br [[IS_UNIQUE]], [[IS_UNIQUE_BB:bb.*]], [[NOT_UNIQUE_BB:bb[0-9]*]] + +// CHECK: [[IS_UNIQUE_BB]]: +// CHECK: [[ITER_ADDR:%.*]] = load [copy] [[ITER]] : $*Optional +// CHECK: [[ITER_BORROW:%.*]] = begin_borrow [[ITER_ADDR]] : $Optional +// CHECK: [[ITER_UNWRAPPED:%.*]] = unchecked_enum_data [[ITER_BORROW]] : $Optional, #Optional.some!enumelt +// CHECK: [[NEXT_ADDR:%.*]] = ref_element_addr [[ITER_UNWRAPPED]] : $Node, #Node.next +// CHECK: [[NEXT_ADDR_ACCESS:%.*]] = begin_access [read] [static] [[NEXT_ADDR]] : $*Optional +// CHECK: [[NEXT_COPY:%.*]] = load [copy] [[NEXT_ADDR_ACCESS]] : $*Optional +// CHECK: end_access [[NEXT_ADDR_ACCESS]] : $*Optional +// CHECK: store [[NEXT_COPY]] to [assign] [[ITER]] : $*Optional +// CHECK: end_borrow [[ITER_BORROW]] : $Optional +// CHECK: destroy_value [[ITER_ADDR]] : $Optional +// CHECK: br [[LOOPBB]] + +// CHECK: [[NOT_UNIQUE_BB]]: +// CHECK: br bb6 + +// CHECK: [[IS_NONE_BB]]: +// CHECK: dealloc_stack [[ITER_COPY_ADDR]] : $*Optional +// CHECK: br [[CLEAN_BB:bb[0-9]+]] + +// CHECK: [[CLEAN_BB]]: +// CHECK: destroy_addr [[ITER]] : $*Optional +// CHECK: dealloc_stack [[ITER]] : $*Optional +// CHECK: [[SELF_NATIVE:%.*]] = unchecked_ref_cast [[SELF]] : $Node to $Builtin.NativeObject +// CHECK: [[SELF_NATIVE_OWNED:%.*]] = unchecked_ownership_conversion [[SELF_NATIVE]] : $Builtin.NativeObject, @guaranteed to @owned +// CHECK: return [[SELF_NATIVE_OWNED]] : $Builtin.NativeObject +// CHECK: } // end sil function '$s16deinit_recursive4NodeCfd' + + + + + +// Non-linearly recursive structures should not get optimized +class Tree { + var left: Tree? + var right: Tree? +} + +// CHECK: sil hidden [ossa] @$s16deinit_recursive4TreeCfd : $@convention(method) (@guaranteed Tree) -> @owned Builtin.NativeObject { +// CHECK: // [[SELF:%.*]] "self" +// CHECK: bb0([[SELF]] : @guaranteed $Tree): +// CHECK: [[LEFT:%.*]] = ref_element_addr [[SELF]] : $Tree, #Tree.left +// CHECK: [[LEFT_ACCESS:%.*]] = begin_access [deinit] [static] [[LEFT]] : $*Optional +// CHECK: destroy_addr [[LEFT_ACCESS]] : $*Optional +// CHECK: end_access [[LEFT_ACCESS]] : $*Optional +// CHECK: [[RIGHT:%.*]] = ref_element_addr [[SELF]] : $Tree, #Tree.right +// CHECK: [[RIGHT_ACCESS:%.*]] = begin_access [deinit] [static] [[RIGHT]] : $*Optional +// CHECK: destroy_addr [[RIGHT_ACCESS]] : $*Optional +// CHECK: end_access [[RIGHT_ACCESS]] : $*Optional // id: %9 +// CHECK: [[SELF_NATIVE:%.*]] = unchecked_ref_cast [[SELF]] : $Tree to $Builtin.NativeObject +// CHECK: [[SELF_NATIVE_OWNED:%.*]] = unchecked_ownership_conversion [[SELF_NATIVE]] : $Builtin.NativeObject, @guaranteed to @owned +// CHECK: return [[SELF_NATIVE_OWNED]] : $Builtin.NativeObject +// CHECK: } // end sil function '$s16deinit_recursive4TreeCfd' diff --git a/test/SILGen/deinit_recursive_no_overflow.swift b/test/SILGen/deinit_recursive_no_overflow.swift new file mode 100644 index 0000000000000..d3de82c35008d --- /dev/null +++ b/test/SILGen/deinit_recursive_no_overflow.swift @@ -0,0 +1,23 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out + +// REQUIRES: executable_test + +class Node { + var next: Node? +} + +var first: Node? = nil +for _ in 1...3_000_000 { + let next = Node() + next.next = first + first = next +} + +@inline(never) +func deallocList() { + first = nil +} +deallocList() From c1bed741eeca783168ee9812e4879ca05dd4a20e Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Mon, 21 Feb 2022 10:39:04 -0800 Subject: [PATCH 006/242] Fix deinit_recursive test by using non-trivial type for elem --- test/SILGen/deinit_recursive.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/SILGen/deinit_recursive.swift b/test/SILGen/deinit_recursive.swift index eafca443ad12e..9d47d1b241bb3 100644 --- a/test/SILGen/deinit_recursive.swift +++ b/test/SILGen/deinit_recursive.swift @@ -1,7 +1,7 @@ // RUN: %target-swift-emit-silgen %s | %FileCheck %s class Node { - var elem: Int64 + var elem: [Int64] = [] var next: Node? } @@ -9,9 +9,9 @@ class Node { // CHECK: [[SELF:%.*]] "self" // CHECK: bb0([[SELF]] : @guaranteed $Node): // CHECK: [[ELEM:%.*]] = ref_element_addr [[SELF]] : $Node, #Node.elem -// CHECK: [[ELEM_ACCESS:%.*]] = begin_access [deinit] [static] [[ELEM]] : $*Int64 -// CHECK: destroy_addr [[ELEM_ACCESS]] : $*Int64 -// CHECK: end_access [[ELEM_ACCESS]] : $*Int64 +// CHECK: [[ELEM_ACCESS:%.*]] = begin_access [deinit] [static] [[ELEM]] : $*Array +// CHECK: destroy_addr [[ELEM_ACCESS]] : $*Array +// CHECK: end_access [[ELEM_ACCESS]] : $*Array // CHECK: [[NIL:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK: [[SELF_NEXT:%.*]] = ref_element_addr [[SELF]] : $Node, #Node.next // CHECK: [[ITER:%.*]] = alloc_stack $Optional From 0f58bf60f154f868c61e08232d42904c65ee0052 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Mon, 21 Feb 2022 11:08:31 -0800 Subject: [PATCH 007/242] Add tests for generics and fix handling of generics --- lib/SILGen/SILGenDestructor.cpp | 10 +-- test/SILGen/deinit_recursive_branching.swift | 23 ++++++ ...ve.swift => deinit_recursive_linear.swift} | 26 ------ .../deinit_recursive_linear_generic.swift | 79 +++++++++++++++++++ 4 files changed, 107 insertions(+), 31 deletions(-) create mode 100644 test/SILGen/deinit_recursive_branching.swift rename test/SILGen/{deinit_recursive.swift => deinit_recursive_linear.swift} (73%) create mode 100644 test/SILGen/deinit_recursive_linear_generic.swift diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index b901faa0ec0aa..20bce107aff64 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -220,9 +220,8 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc, } } -llvm::SmallSetVector findRecursiveLinks(DeclContext *DC, - ClassDecl *cd) { - auto SelfTy = DC->mapTypeIntoContext(cd->getDeclaredInterfaceType()); +llvm::SmallSetVector findRecursiveLinks(ClassDecl *cd) { + auto SelfTy = cd->getDeclaredInterfaceType(); // Collect all stored properties that would form a recursive structure, // so we can remove the recursion and prevent the call stack from @@ -262,7 +261,8 @@ void SILGenFunction::emitRecursiveChainDestruction( // var iter = self.link // self.link = nil - auto Ty = getTypeLowering(vd->getInterfaceType()).getLoweredType(); + auto Ty = getTypeLowering(F.mapTypeIntoContext(vd->getInterfaceType())) + .getLoweredType(); auto optionalNone = B.createOptionalNone(cleanupLoc, Ty); SILValue varAddr = B.createRefElementAddr(cleanupLoc, selfValue.getValue(), vd, Ty.getAddressType()); @@ -375,7 +375,7 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, finishBB); } - auto recursiveLinks = findRecursiveLinks(F.getDeclContext(), cd); + auto recursiveLinks = findRecursiveLinks(cd); /// Destroy all members. { diff --git a/test/SILGen/deinit_recursive_branching.swift b/test/SILGen/deinit_recursive_branching.swift new file mode 100644 index 0000000000000..9870f13e5a0e3 --- /dev/null +++ b/test/SILGen/deinit_recursive_branching.swift @@ -0,0 +1,23 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +// Non-linearly recursive structures should not get optimized +class Tree { + var left: Tree? + var right: Tree? +} + +// CHECK: sil hidden [ossa] @$s16deinit_recursive4TreeCfd : $@convention(method) (@guaranteed Tree) -> @owned Builtin.NativeObject { +// CHECK: // [[SELF:%.*]] "self" +// CHECK: bb0([[SELF]] : @guaranteed $Tree): +// CHECK: [[LEFT:%.*]] = ref_element_addr [[SELF]] : $Tree, #Tree.left +// CHECK: [[LEFT_ACCESS:%.*]] = begin_access [deinit] [static] [[LEFT]] : $*Optional +// CHECK: destroy_addr [[LEFT_ACCESS]] : $*Optional +// CHECK: end_access [[LEFT_ACCESS]] : $*Optional +// CHECK: [[RIGHT:%.*]] = ref_element_addr [[SELF]] : $Tree, #Tree.right +// CHECK: [[RIGHT_ACCESS:%.*]] = begin_access [deinit] [static] [[RIGHT]] : $*Optional +// CHECK: destroy_addr [[RIGHT_ACCESS]] : $*Optional +// CHECK: end_access [[RIGHT_ACCESS]] : $*Optional // id: %9 +// CHECK: [[SELF_NATIVE:%.*]] = unchecked_ref_cast [[SELF]] : $Tree to $Builtin.NativeObject +// CHECK: [[SELF_NATIVE_OWNED:%.*]] = unchecked_ownership_conversion [[SELF_NATIVE]] : $Builtin.NativeObject, @guaranteed to @owned +// CHECK: return [[SELF_NATIVE_OWNED]] : $Builtin.NativeObject +// CHECK: } // end sil function '$s16deinit_recursive4TreeCfd' \ No newline at end of file diff --git a/test/SILGen/deinit_recursive.swift b/test/SILGen/deinit_recursive_linear.swift similarity index 73% rename from test/SILGen/deinit_recursive.swift rename to test/SILGen/deinit_recursive_linear.swift index 9d47d1b241bb3..54746596dfe3c 100644 --- a/test/SILGen/deinit_recursive.swift +++ b/test/SILGen/deinit_recursive_linear.swift @@ -63,29 +63,3 @@ class Node { // CHECK: [[SELF_NATIVE_OWNED:%.*]] = unchecked_ownership_conversion [[SELF_NATIVE]] : $Builtin.NativeObject, @guaranteed to @owned // CHECK: return [[SELF_NATIVE_OWNED]] : $Builtin.NativeObject // CHECK: } // end sil function '$s16deinit_recursive4NodeCfd' - - - - - -// Non-linearly recursive structures should not get optimized -class Tree { - var left: Tree? - var right: Tree? -} - -// CHECK: sil hidden [ossa] @$s16deinit_recursive4TreeCfd : $@convention(method) (@guaranteed Tree) -> @owned Builtin.NativeObject { -// CHECK: // [[SELF:%.*]] "self" -// CHECK: bb0([[SELF]] : @guaranteed $Tree): -// CHECK: [[LEFT:%.*]] = ref_element_addr [[SELF]] : $Tree, #Tree.left -// CHECK: [[LEFT_ACCESS:%.*]] = begin_access [deinit] [static] [[LEFT]] : $*Optional -// CHECK: destroy_addr [[LEFT_ACCESS]] : $*Optional -// CHECK: end_access [[LEFT_ACCESS]] : $*Optional -// CHECK: [[RIGHT:%.*]] = ref_element_addr [[SELF]] : $Tree, #Tree.right -// CHECK: [[RIGHT_ACCESS:%.*]] = begin_access [deinit] [static] [[RIGHT]] : $*Optional -// CHECK: destroy_addr [[RIGHT_ACCESS]] : $*Optional -// CHECK: end_access [[RIGHT_ACCESS]] : $*Optional // id: %9 -// CHECK: [[SELF_NATIVE:%.*]] = unchecked_ref_cast [[SELF]] : $Tree to $Builtin.NativeObject -// CHECK: [[SELF_NATIVE_OWNED:%.*]] = unchecked_ownership_conversion [[SELF_NATIVE]] : $Builtin.NativeObject, @guaranteed to @owned -// CHECK: return [[SELF_NATIVE_OWNED]] : $Builtin.NativeObject -// CHECK: } // end sil function '$s16deinit_recursive4TreeCfd' diff --git a/test/SILGen/deinit_recursive_linear_generic.swift b/test/SILGen/deinit_recursive_linear_generic.swift new file mode 100644 index 0000000000000..1c85bb89fde01 --- /dev/null +++ b/test/SILGen/deinit_recursive_linear_generic.swift @@ -0,0 +1,79 @@ +// RUN: %target-swift-emit-silgen %s | %FileCheck %s + +class Node { + var next: Node? +} + +// CHECK: sil hidden [ossa] @$s31deinit_recursive_linear_generic4NodeCfd : $@convention(method) (@guaranteed Node) -> @owned Builtin.NativeObject { +// CHECK: [[SELF:%.*]] "self" +// CHECK: bb0([[SELF]] : @guaranteed $Node): +// CHECK: [[NIL:%.*]] = enum $Optional>, #Optional.none!enumelt +// CHECK: [[SELF_NEXT:%.*]] = ref_element_addr [[SELF]] : $Node, #Node.next +// CHECK: [[ITER:%.*]] = alloc_stack $Optional> +// CHECK: [[SELF_NEXT_ACCESS:%.*]] = begin_access [modify] [static] [[SELF_NEXT]] : $*Optional> +// CHECK: [[SELF_NEXT_COPY:%.*]] = load [copy] [[SELF_NEXT_ACCESS]] : $*Optional> +// CHECK: store [[NIL]] to [assign] [[SELF_NEXT_ACCESS]] : $*Optional> +// CHECK: end_access [[SELF_NEXT_ACCESS]] : $*Optional> +// CHECK: store [[SELF_NEXT_COPY]] to [init] [[ITER]] : $*Optional> +// CHECK: br [[LOOPBB:bb.*]] // + +// CHECK: [[LOOPBB]]: +// CHECK: [[ITER_ADDR:%.*]] = load [copy] [[ITER]] : $*Optional> +// CHECK: [[ITER_COPY:%.*]] = copy_value [[ITER_ADDR]] : $Optional> +// CHECK: [[ITER_COPY_ADDR:%.*]] = alloc_stack $Optional> +// CHECK: store [[ITER_COPY]] to [init] [[ITER_COPY_ADDR]] : $*Optional> +// CHECK: destroy_value [[ITER_ADDR]] : $Optional> +// CHECK: switch_enum_addr [[ITER_COPY_ADDR]] : $*Optional>, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] + +// CHECK: [[IS_SOME_BB]]: +// CHECK: destroy_addr [[ITER_COPY_ADDR]] : $*Optional> +// CHECK: dealloc_stack [[ITER_COPY_ADDR]] : $*Optional> +// CHECK: [[IS_UNIQUE:%.*]] = is_unique [[ITER]] : $*Optional> +// CHECK: cond_br [[IS_UNIQUE]], [[IS_UNIQUE_BB:bb.*]], [[NOT_UNIQUE_BB:bb[0-9]*]] + +// CHECK: [[IS_UNIQUE_BB]]: +// CHECK: [[ITER_ADDR:%.*]] = load [copy] [[ITER]] : $*Optional> +// CHECK: [[ITER_BORROW:%.*]] = begin_borrow [[ITER_ADDR]] : $Optional> +// CHECK: [[ITER_UNWRAPPED:%.*]] = unchecked_enum_data [[ITER_BORROW]] : $Optional>, #Optional.some!enumelt +// CHECK: [[NEXT_ADDR:%.*]] = ref_element_addr [[ITER_UNWRAPPED]] : $Node, #Node.next +// CHECK: [[NEXT_ADDR_ACCESS:%.*]] = begin_access [read] [static] [[NEXT_ADDR]] : $*Optional> +// CHECK: [[NEXT_COPY:%.*]] = load [copy] [[NEXT_ADDR_ACCESS]] : $*Optional> +// CHECK: end_access [[NEXT_ADDR_ACCESS]] : $*Optional> +// CHECK: store [[NEXT_COPY]] to [assign] [[ITER]] : $*Optional> +// CHECK: end_borrow [[ITER_BORROW]] : $Optional> +// CHECK: destroy_value [[ITER_ADDR]] : $Optional> +// CHECK: br [[LOOPBB]] + +// CHECK: [[NOT_UNIQUE_BB]]: +// CHECK: br bb6 + +// CHECK: [[IS_NONE_BB]]: +// CHECK: dealloc_stack [[ITER_COPY_ADDR]] : $*Optional> +// CHECK: br [[CLEAN_BB:bb[0-9]+]] + +// CHECK: [[CLEAN_BB]]: +// CHECK: destroy_addr [[ITER]] : $*Optional> +// CHECK: dealloc_stack [[ITER]] : $*Optional> +// CHECK: [[SELF_NATIVE:%.*]] = unchecked_ref_cast [[SELF]] : $Node to $Builtin.NativeObject +// CHECK: [[SELF_NATIVE_OWNED:%.*]] = unchecked_ownership_conversion [[SELF_NATIVE]] : $Builtin.NativeObject, @guaranteed to @owned +// CHECK: return [[SELF_NATIVE_OWNED]] : $Builtin.NativeObject +// CHECK: } // end sil function '$s31deinit_recursive_linear_generic4NodeCfd' + + +// Types of `self` and `next` don't match, so this should not be optimized +class Node2 { + var next: Node2? +} + +// CHECK: sil hidden [ossa] @$s31deinit_recursive_linear_generic5Node2Cfd : $@convention(method) (@guaranteed Node2) -> @owned Builtin.NativeObject { +// CHECK: [[SELF:%.*]] "self" +// CHECK: bb0([[SELF]] : @guaranteed $Node2): +// CHECK: debug_value [[SELF]] : $Node2, let, name "self", argno 1, implicit +// CHECK: [[NEXT_ADDR:%.*]] = ref_element_addr [[SELF]] : $Node2, #Node2.next +// CHECK: [[NEXT_ACCESS:%.*]] = begin_access [deinit] [static] [[NEXT_ADDR]] : $*Optional> +// CHECK: destroy_addr [[NEXT_ACCESS]] : $*Optional> +// CHECK: end_access [[NEXT_ACCESS]] : $*Optional> +// CHECK: [[SELF_NATIVE:%.*]] = unchecked_ref_cast [[SELF]] : $Node2 to $Builtin.NativeObject +// CHECK: [[SELF_OWNED:%.*]] = unchecked_ownership_conversion [[SELF_NATIVE]] : $Builtin.NativeObject, @guaranteed to @owned +// CHECK: return [[SELF_OWNED]] : $Builtin.NativeObject +// CHECK: } // end sil function '$s31deinit_recursive_linear_generic5Node2Cfd' \ No newline at end of file From 32616ee53443832e66bb124d2ada6f51f16d90e7 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Mon, 21 Feb 2022 13:54:41 -0800 Subject: [PATCH 008/242] Fix symbols in tests after reorganizing --- test/SILGen/deinit_recursive_branching.swift | 4 ++-- test/SILGen/deinit_recursive_linear.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/SILGen/deinit_recursive_branching.swift b/test/SILGen/deinit_recursive_branching.swift index 9870f13e5a0e3..fd4070a2f60b2 100644 --- a/test/SILGen/deinit_recursive_branching.swift +++ b/test/SILGen/deinit_recursive_branching.swift @@ -6,7 +6,7 @@ class Tree { var right: Tree? } -// CHECK: sil hidden [ossa] @$s16deinit_recursive4TreeCfd : $@convention(method) (@guaranteed Tree) -> @owned Builtin.NativeObject { +// CHECK: sil hidden [ossa] @$s26deinit_recursive_branching4TreeCfd : $@convention(method) (@guaranteed Tree) -> @owned Builtin.NativeObject { // CHECK: // [[SELF:%.*]] "self" // CHECK: bb0([[SELF]] : @guaranteed $Tree): // CHECK: [[LEFT:%.*]] = ref_element_addr [[SELF]] : $Tree, #Tree.left @@ -20,4 +20,4 @@ class Tree { // CHECK: [[SELF_NATIVE:%.*]] = unchecked_ref_cast [[SELF]] : $Tree to $Builtin.NativeObject // CHECK: [[SELF_NATIVE_OWNED:%.*]] = unchecked_ownership_conversion [[SELF_NATIVE]] : $Builtin.NativeObject, @guaranteed to @owned // CHECK: return [[SELF_NATIVE_OWNED]] : $Builtin.NativeObject -// CHECK: } // end sil function '$s16deinit_recursive4TreeCfd' \ No newline at end of file +// CHECK: } // end sil function '$s26deinit_recursive_branching4TreeCfd' \ No newline at end of file diff --git a/test/SILGen/deinit_recursive_linear.swift b/test/SILGen/deinit_recursive_linear.swift index 54746596dfe3c..2e3544e587810 100644 --- a/test/SILGen/deinit_recursive_linear.swift +++ b/test/SILGen/deinit_recursive_linear.swift @@ -5,7 +5,7 @@ class Node { var next: Node? } -// CHECK: sil hidden [ossa] @$s16deinit_recursive4NodeCfd : $@convention(method) (@guaranteed Node) -> @owned Builtin.NativeObject { +// CHECK: sil hidden [ossa] @$s23deinit_recursive_linear4NodeCfd : $@convention(method) (@guaranteed Node) -> @owned Builtin.NativeObject { // CHECK: [[SELF:%.*]] "self" // CHECK: bb0([[SELF]] : @guaranteed $Node): // CHECK: [[ELEM:%.*]] = ref_element_addr [[SELF]] : $Node, #Node.elem @@ -62,4 +62,4 @@ class Node { // CHECK: [[SELF_NATIVE:%.*]] = unchecked_ref_cast [[SELF]] : $Node to $Builtin.NativeObject // CHECK: [[SELF_NATIVE_OWNED:%.*]] = unchecked_ownership_conversion [[SELF_NATIVE]] : $Builtin.NativeObject, @guaranteed to @owned // CHECK: return [[SELF_NATIVE_OWNED]] : $Builtin.NativeObject -// CHECK: } // end sil function '$s16deinit_recursive4NodeCfd' +// CHECK: } // end sil function '$s23deinit_recursive_linear4NodeCfd' From 542c395e0025b98ce998f946c84b92d8ad63c633 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Tue, 22 Feb 2022 14:59:35 -0800 Subject: [PATCH 009/242] Addressed more review feedback --- lib/SILGen/SILGenDestructor.cpp | 199 ++++++++---------- lib/SILGen/SILGenFunction.h | 15 +- test/SILGen/deinit_recursive_linear.swift | 24 +-- .../deinit_recursive_linear_generic.swift | 24 +-- 4 files changed, 112 insertions(+), 150 deletions(-) diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 20bce107aff64..68da164a733e5 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -220,131 +220,111 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc, } } -llvm::SmallSetVector findRecursiveLinks(ClassDecl *cd) { +void findRecursiveLinks(ClassDecl *cd, llvm::SmallSetVector &result) { auto SelfTy = cd->getDeclaredInterfaceType(); // Collect all stored properties that would form a recursive structure, // so we can remove the recursion and prevent the call stack from // overflowing. - llvm::SmallSetVector recursiveLinks; for (VarDecl *vd : cd->getStoredProperties()) { auto Ty = vd->getInterfaceType()->getOptionalObjectType(); if (Ty && Ty->getCanonicalType() == SelfTy->getCanonicalType()) { - recursiveLinks.insert(vd); + result.insert(vd); } } // NOTE: Right now we only optimize linear recursion, so if there is more than // one link, clear out the set and don't perform any recursion optimization. - if (recursiveLinks.size() > 1) { - recursiveLinks.clear(); + if (result.size() > 1) { + result.clear(); } - - return recursiveLinks; } -void SILGenFunction::emitRecursiveChainDestruction( - ManagedValue selfValue, ClassDecl *cd, - SmallSetVector recursiveLinks, CleanupLocation cleanupLoc) { +void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, + ClassDecl *cd, + VarDecl *recursiveLink, + CleanupLocation cleanupLoc) { auto SelfTy = F.mapTypeIntoContext(cd->getDeclaredInterfaceType()); - assert(recursiveLinks.size() <= 1 && "Only linear recursion supported."); - auto SelfTyLowered = getTypeLowering(SelfTy).getLoweredType(); - for (VarDecl *vd : recursiveLinks) { - SILBasicBlock *cleanBB = createBasicBlock(); - SILBasicBlock *noneBB = createBasicBlock(); - SILBasicBlock *notUniqueBB = createBasicBlock(); - SILBasicBlock *uniqueBB = createBasicBlock(); - SILBasicBlock *someBB = createBasicBlock(); - SILBasicBlock *loopBB = createBasicBlock(); - - // var iter = self.link - // self.link = nil - auto Ty = getTypeLowering(F.mapTypeIntoContext(vd->getInterfaceType())) - .getLoweredType(); - auto optionalNone = B.createOptionalNone(cleanupLoc, Ty); - SILValue varAddr = B.createRefElementAddr(cleanupLoc, selfValue.getValue(), - vd, Ty.getAddressType()); - auto iterAddr = B.createAllocStack(cleanupLoc, Ty); - SILValue addr = - B.createBeginAccess(cleanupLoc, varAddr, SILAccessKind::Modify, - SILAccessEnforcement::Static, - false /*noNestedConflict*/, false /*fromBuiltin*/); - SILValue iter = - B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); - B.createStore(cleanupLoc, optionalNone, addr, - StoreOwnershipQualifier::Assign); - B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); - B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Init); - - B.createBranch(cleanupLoc, loopBB); - - // while iter != nil { - B.emitBlock(loopBB); - SILValue operand = - B.createLoad(cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy); - auto operandCopy = B.createCopyValue(cleanupLoc, operand); - auto operandAddr = B.createAllocStack(cleanupLoc, Ty); - B.createStore(cleanupLoc, operandCopy, operandAddr, - StoreOwnershipQualifier::Init); - B.createDestroyValue(cleanupLoc, operand); - B.createSwitchEnumAddr( - cleanupLoc, operandAddr, nullptr, - {{getASTContext().getOptionalSomeDecl(), someBB}, - {std::make_pair(getASTContext().getOptionalNoneDecl(), noneBB)}}); - - // if isKnownUniquelyReferenced(&iter) { - B.emitBlock(someBB); - B.createDestroyAddr(cleanupLoc, operandAddr); - B.createDeallocStack(cleanupLoc, operandAddr); - auto isUnique = B.createIsUnique(cleanupLoc, iterAddr); - B.createCondBranch(cleanupLoc, isUnique, uniqueBB, notUniqueBB); - - // we have a uniquely referenced link, so we need to deinit - B.emitBlock(uniqueBB); - - // NOTE: We increment the ref count of the tail instead of unlinking it, - // because custom deinit implementations of subclasses may access - // it and it would be semantically wrong to unset it before that. - // Making the tail non-uniquely referenced prevents the recursion. - - // let tail = iter.unsafelyUnwrapped.next - // iter = tail - SILValue _iter = - B.createLoad(cleanupLoc, iterAddr, LoadOwnershipQualifier::Copy); - auto iterBorrow = B.createBeginBorrow(cleanupLoc, _iter); - auto *link = B.createUncheckedEnumData( - cleanupLoc, iterBorrow, getASTContext().getOptionalSomeDecl(), - SelfTyLowered); - varAddr = B.createRefElementAddr(cleanupLoc, link, vd, Ty.getAddressType()); - - addr = B.createBeginAccess( - cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static, - false /* noNestedConflict */, false /*fromBuiltin*/); - iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); - B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); - B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign); - B.createEndBorrow(cleanupLoc, iterBorrow); - - B.createDestroyValue(cleanupLoc, _iter); - - B.createBranch(cleanupLoc, loopBB); - - // the next link in the chain is not unique, so we are done here - B.emitBlock(notUniqueBB); - B.createBranch(cleanupLoc, cleanBB); - - // we reached the end of the chain - B.emitBlock(noneBB); - B.createDeallocStack(cleanupLoc, operandAddr); - B.createBranch(cleanupLoc, cleanBB); - - B.emitBlock(cleanBB); - B.createDestroyAddr(cleanupLoc, iterAddr); - B.createDeallocStack(cleanupLoc, iterAddr); - } + SILBasicBlock *cleanBB = createBasicBlock(); + SILBasicBlock *noneBB = createBasicBlock(); + SILBasicBlock *notUniqueBB = createBasicBlock(); + SILBasicBlock *uniqueBB = createBasicBlock(); + SILBasicBlock *someBB = createBasicBlock(); + SILBasicBlock *loopBB = createBasicBlock(); + + // var iter = self.link + // self.link = nil + auto Ty = getTypeLowering(F.mapTypeIntoContext(recursiveLink->getInterfaceType())).getLoweredType(); + auto optionalNone = B.createOptionalNone(cleanupLoc, Ty); + SILValue varAddr = + B.createRefElementAddr(cleanupLoc, selfValue.getValue(), recursiveLink, + Ty.getAddressType()); + auto iterAddr = B.createAllocStack(cleanupLoc, Ty); + SILValue addr = B.createBeginAccess( + cleanupLoc, varAddr, SILAccessKind::Modify, SILAccessEnforcement::Static, + true /*noNestedConflict*/, false /*fromBuiltin*/); + SILValue iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Take); + B.createStore(cleanupLoc, optionalNone, addr, StoreOwnershipQualifier::Init); + B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); + B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Init); + + B.createBranch(cleanupLoc, loopBB); + + // while iter != nil { + B.emitBlock(loopBB); + B.createSwitchEnumAddr( + cleanupLoc, iterAddr, nullptr, + {{getASTContext().getOptionalSomeDecl(), someBB}, + {std::make_pair(getASTContext().getOptionalNoneDecl(), noneBB)}}); + + // if isKnownUniquelyReferenced(&iter) { + B.emitBlock(someBB); + auto isUnique = B.createIsUnique(cleanupLoc, iterAddr); + B.createCondBranch(cleanupLoc, isUnique, uniqueBB, notUniqueBB); + + // we have a uniquely referenced link, so we need to deinit + B.emitBlock(uniqueBB); + + // NOTE: We increment the ref count of the tail instead of unlinking it, + // because custom deinit implementations of subclasses may access + // it and it would be semantically wrong to unset it before that. + // Making the tail non-uniquely referenced prevents the recursion. + + // let tail = iter.unsafelyUnwrapped.next + // iter = tail + SILValue iterBorrow = B.createLoadBorrow(cleanupLoc, iterAddr); + auto *link = B.createUncheckedEnumData(cleanupLoc, iterBorrow, + getASTContext().getOptionalSomeDecl(), + SelfTyLowered); + + varAddr = B.createRefElementAddr(cleanupLoc, link, recursiveLink, + Ty.getAddressType()); + + addr = B.createBeginAccess( + cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static, + true /* noNestedConflict */, false /*fromBuiltin*/); + iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); + B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); + B.createEndBorrow(cleanupLoc, iterBorrow); + + B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign); + + B.createBranch(cleanupLoc, loopBB); + + // the next link in the chain is not unique, so we are done here + B.emitBlock(notUniqueBB); + B.createBranch(cleanupLoc, cleanBB); + + // we reached the end of the chain + B.emitBlock(noneBB); + B.createBranch(cleanupLoc, cleanBB); + + B.emitBlock(cleanBB); + B.createDestroyAddr(cleanupLoc, iterAddr); + B.createDeallocStack(cleanupLoc, iterAddr); } void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, @@ -375,7 +355,8 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, finishBB); } - auto recursiveLinks = findRecursiveLinks(cd); + llvm::SmallSetVector recursiveLinks; + findRecursiveLinks(cd, recursiveLinks); /// Destroy all members. { @@ -388,8 +369,10 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, destroyClassMember(cleanupLoc, selfValue, vd); } - if (!recursiveLinks.empty()) - emitRecursiveChainDestruction(selfValue, cd, recursiveLinks, cleanupLoc); + if (!recursiveLinks.empty()) { + assert(recursiveLinks.size() == 1 && "Only linear recursion supported."); + emitRecursiveChainDestruction(selfValue, cd, recursiveLinks[0], cleanupLoc); + } if (finishBB) B.createBranch(cleanupLoc, finishBB); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index cc45eb4a2cfe9..e8493f2b3a5cd 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -680,17 +680,16 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction void emitClassMemberDestruction(ManagedValue selfValue, ClassDecl *cd, CleanupLocation cleanupLoc); - /// Generates code to destroy recursive data structures, without building - /// up the call stack. + /// Generates code to destroy linearly recursive data structures, without + /// building up the call stack. /// /// \param selfValue The 'self' value. /// \param cd The class declaration whose members are being destroyed. - /// \param recursiveLinks The set of stored properties that form the - /// recursive data structure. - void emitRecursiveChainDestruction( - ManagedValue selfValue, ClassDecl *cd, - llvm::SmallSetVector recursiveLinks, - CleanupLocation cleanupLoc); + /// \param recursiveLink The property that forms the recursive structure. + void emitRecursiveChainDestruction(ManagedValue selfValue, + ClassDecl *cd, + VarDecl* recursiveLink, + CleanupLocation cleanupLoc); /// Generates a thunk from a foreign function to the native Swift convention. void emitForeignToNativeThunk(SILDeclRef thunk); diff --git a/test/SILGen/deinit_recursive_linear.swift b/test/SILGen/deinit_recursive_linear.swift index 2e3544e587810..b36d9b5760783 100644 --- a/test/SILGen/deinit_recursive_linear.swift +++ b/test/SILGen/deinit_recursive_linear.swift @@ -15,45 +15,35 @@ class Node { // CHECK: [[NIL:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK: [[SELF_NEXT:%.*]] = ref_element_addr [[SELF]] : $Node, #Node.next // CHECK: [[ITER:%.*]] = alloc_stack $Optional -// CHECK: [[SELF_NEXT_ACCESS:%.*]] = begin_access [modify] [static] [[SELF_NEXT]] : $*Optional -// CHECK: [[SELF_NEXT_COPY:%.*]] = load [copy] [[SELF_NEXT_ACCESS]] : $*Optional -// CHECK: store [[NIL]] to [assign] [[SELF_NEXT_ACCESS]] : $*Optional +// CHECK: [[SELF_NEXT_ACCESS:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[SELF_NEXT]] : $*Optional +// CHECK: [[SELF_NEXT_COPY:%.*]] = load [take] [[SELF_NEXT_ACCESS]] : $*Optional +// CHECK: store [[NIL]] to [init] [[SELF_NEXT_ACCESS]] : $*Optional // CHECK: end_access [[SELF_NEXT_ACCESS]] : $*Optional // CHECK: store [[SELF_NEXT_COPY]] to [init] [[ITER]] : $*Optional // CHECK: br [[LOOPBB:bb.*]] // // CHECK: [[LOOPBB]]: -// CHECK: [[ITER_ADDR:%.*]] = load [copy] [[ITER]] : $*Optional -// CHECK: [[ITER_COPY:%.*]] = copy_value [[ITER_ADDR]] : $Optional -// CHECK: [[ITER_COPY_ADDR:%.*]] = alloc_stack $Optional -// CHECK: store [[ITER_COPY]] to [init] [[ITER_COPY_ADDR]] : $*Optional -// CHECK: destroy_value [[ITER_ADDR]] : $Optional -// CHECK: switch_enum_addr [[ITER_COPY_ADDR]] : $*Optional, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] +// CHECK: switch_enum_addr [[ITER]] : $*Optional, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] // CHECK: [[IS_SOME_BB]]: -// CHECK: destroy_addr [[ITER_COPY_ADDR]] : $*Optional -// CHECK: dealloc_stack [[ITER_COPY_ADDR]] : $*Optional // CHECK: [[IS_UNIQUE:%.*]] = is_unique [[ITER]] : $*Optional // CHECK: cond_br [[IS_UNIQUE]], [[IS_UNIQUE_BB:bb.*]], [[NOT_UNIQUE_BB:bb[0-9]*]] // CHECK: [[IS_UNIQUE_BB]]: -// CHECK: [[ITER_ADDR:%.*]] = load [copy] [[ITER]] : $*Optional -// CHECK: [[ITER_BORROW:%.*]] = begin_borrow [[ITER_ADDR]] : $Optional +// CHECK: [[ITER_BORROW:%.*]] = load_borrow [[ITER]] : $*Optional // CHECK: [[ITER_UNWRAPPED:%.*]] = unchecked_enum_data [[ITER_BORROW]] : $Optional, #Optional.some!enumelt // CHECK: [[NEXT_ADDR:%.*]] = ref_element_addr [[ITER_UNWRAPPED]] : $Node, #Node.next -// CHECK: [[NEXT_ADDR_ACCESS:%.*]] = begin_access [read] [static] [[NEXT_ADDR]] : $*Optional +// CHECK: [[NEXT_ADDR_ACCESS:%.*]] = begin_access [read] [static] [no_nested_conflict] [[NEXT_ADDR]] : $*Optional // CHECK: [[NEXT_COPY:%.*]] = load [copy] [[NEXT_ADDR_ACCESS]] : $*Optional // CHECK: end_access [[NEXT_ADDR_ACCESS]] : $*Optional -// CHECK: store [[NEXT_COPY]] to [assign] [[ITER]] : $*Optional // CHECK: end_borrow [[ITER_BORROW]] : $Optional -// CHECK: destroy_value [[ITER_ADDR]] : $Optional +// CHECK: store [[NEXT_COPY]] to [assign] [[ITER]] : $*Optional // CHECK: br [[LOOPBB]] // CHECK: [[NOT_UNIQUE_BB]]: // CHECK: br bb6 // CHECK: [[IS_NONE_BB]]: -// CHECK: dealloc_stack [[ITER_COPY_ADDR]] : $*Optional // CHECK: br [[CLEAN_BB:bb[0-9]+]] // CHECK: [[CLEAN_BB]]: diff --git a/test/SILGen/deinit_recursive_linear_generic.swift b/test/SILGen/deinit_recursive_linear_generic.swift index 1c85bb89fde01..cd0b993cdb2dc 100644 --- a/test/SILGen/deinit_recursive_linear_generic.swift +++ b/test/SILGen/deinit_recursive_linear_generic.swift @@ -10,45 +10,35 @@ class Node { // CHECK: [[NIL:%.*]] = enum $Optional>, #Optional.none!enumelt // CHECK: [[SELF_NEXT:%.*]] = ref_element_addr [[SELF]] : $Node, #Node.next // CHECK: [[ITER:%.*]] = alloc_stack $Optional> -// CHECK: [[SELF_NEXT_ACCESS:%.*]] = begin_access [modify] [static] [[SELF_NEXT]] : $*Optional> -// CHECK: [[SELF_NEXT_COPY:%.*]] = load [copy] [[SELF_NEXT_ACCESS]] : $*Optional> -// CHECK: store [[NIL]] to [assign] [[SELF_NEXT_ACCESS]] : $*Optional> +// CHECK: [[SELF_NEXT_ACCESS:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[SELF_NEXT]] : $*Optional> +// CHECK: [[SELF_NEXT_COPY:%.*]] = load [take] [[SELF_NEXT_ACCESS]] : $*Optional> +// CHECK: store [[NIL]] to [init] [[SELF_NEXT_ACCESS]] : $*Optional> // CHECK: end_access [[SELF_NEXT_ACCESS]] : $*Optional> // CHECK: store [[SELF_NEXT_COPY]] to [init] [[ITER]] : $*Optional> // CHECK: br [[LOOPBB:bb.*]] // // CHECK: [[LOOPBB]]: -// CHECK: [[ITER_ADDR:%.*]] = load [copy] [[ITER]] : $*Optional> -// CHECK: [[ITER_COPY:%.*]] = copy_value [[ITER_ADDR]] : $Optional> -// CHECK: [[ITER_COPY_ADDR:%.*]] = alloc_stack $Optional> -// CHECK: store [[ITER_COPY]] to [init] [[ITER_COPY_ADDR]] : $*Optional> -// CHECK: destroy_value [[ITER_ADDR]] : $Optional> -// CHECK: switch_enum_addr [[ITER_COPY_ADDR]] : $*Optional>, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] +// CHECK: switch_enum_addr [[ITER]] : $*Optional>, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] // CHECK: [[IS_SOME_BB]]: -// CHECK: destroy_addr [[ITER_COPY_ADDR]] : $*Optional> -// CHECK: dealloc_stack [[ITER_COPY_ADDR]] : $*Optional> // CHECK: [[IS_UNIQUE:%.*]] = is_unique [[ITER]] : $*Optional> // CHECK: cond_br [[IS_UNIQUE]], [[IS_UNIQUE_BB:bb.*]], [[NOT_UNIQUE_BB:bb[0-9]*]] // CHECK: [[IS_UNIQUE_BB]]: -// CHECK: [[ITER_ADDR:%.*]] = load [copy] [[ITER]] : $*Optional> -// CHECK: [[ITER_BORROW:%.*]] = begin_borrow [[ITER_ADDR]] : $Optional> +// CHECK: [[ITER_BORROW:%.*]] = load_borrow [[ITER]] : $*Optional> // CHECK: [[ITER_UNWRAPPED:%.*]] = unchecked_enum_data [[ITER_BORROW]] : $Optional>, #Optional.some!enumelt // CHECK: [[NEXT_ADDR:%.*]] = ref_element_addr [[ITER_UNWRAPPED]] : $Node, #Node.next -// CHECK: [[NEXT_ADDR_ACCESS:%.*]] = begin_access [read] [static] [[NEXT_ADDR]] : $*Optional> +// CHECK: [[NEXT_ADDR_ACCESS:%.*]] = begin_access [read] [static] [no_nested_conflict] [[NEXT_ADDR]] : $*Optional> // CHECK: [[NEXT_COPY:%.*]] = load [copy] [[NEXT_ADDR_ACCESS]] : $*Optional> // CHECK: end_access [[NEXT_ADDR_ACCESS]] : $*Optional> -// CHECK: store [[NEXT_COPY]] to [assign] [[ITER]] : $*Optional> // CHECK: end_borrow [[ITER_BORROW]] : $Optional> -// CHECK: destroy_value [[ITER_ADDR]] : $Optional> +// CHECK: store [[NEXT_COPY]] to [assign] [[ITER]] : $*Optional> // CHECK: br [[LOOPBB]] // CHECK: [[NOT_UNIQUE_BB]]: // CHECK: br bb6 // CHECK: [[IS_NONE_BB]]: -// CHECK: dealloc_stack [[ITER_COPY_ADDR]] : $*Optional> // CHECK: br [[CLEAN_BB:bb[0-9]+]] // CHECK: [[CLEAN_BB]]: From 9a82a8f2e95d2359d633f2918460a331b9d02a31 Mon Sep 17 00:00:00 2001 From: Richard Wei Date: Mon, 28 Feb 2022 12:36:25 -0800 Subject: [PATCH 010/242] [update-checkout] Switch string processing to swift/main Switch to using swift/main as the integration branch for apple/swift-experimental-string-processing. Integration process documentation: apple/swift-experimental-string-processing#170 Friend PR: apple/swift-experimental-string-processing#192 --- utils/update_checkout/update-checkout-config.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/update_checkout/update-checkout-config.json b/utils/update_checkout/update-checkout-config.json index c8eaebce628f1..e1f2ce2feb2b0 100644 --- a/utils/update_checkout/update-checkout-config.json +++ b/utils/update_checkout/update-checkout-config.json @@ -123,7 +123,7 @@ "swift-cmark-gfm": "gfm", "swift-nio": "2.31.2", "swift-nio-ssl": "2.15.0", - "swift-experimental-string-processing": "dev/8" + "swift-experimental-string-processing": "swift/main" } }, "rebranch": { @@ -157,7 +157,7 @@ "sourcekit-lsp": "main", "swift-format": "main", "swift-installer-scripts": "main", - "swift-experimental-string-processing": "dev/8" + "swift-experimental-string-processing": "swift/main" } }, "release/5.6": { @@ -308,7 +308,7 @@ "sourcekit-lsp": "main", "swift-format": "main", "swift-installer-scripts": "main", - "swift-experimental-string-processing": "dev/8" + "swift-experimental-string-processing": "swift/main" } }, "release/5.4": { From 06aa3349dc10c74e86de3355aaee81e876f87919 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Thu, 3 Mar 2022 08:36:18 -0800 Subject: [PATCH 011/242] Address review feedback --- lib/SILGen/SILGenDestructor.cpp | 132 +++++++++++------- lib/SILGen/SILGenFunction.h | 7 + .../deinit_recursive_no_overflow.swift | 7 +- test/SILGen/deinit_recursive_linear.swift | 6 +- .../deinit_recursive_linear_generic.swift | 6 +- 5 files changed, 102 insertions(+), 56 deletions(-) rename test/{SILGen => Interpreter}/deinit_recursive_no_overflow.swift (60%) diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 68da164a733e5..9a76be26f1b29 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -14,6 +14,7 @@ #include "RValue.h" #include "SILGenFunction.h" #include "SILGenFunctionBuilder.h" +#include "SwitchEnumBuilder.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/SubstitutionMap.h" #include "swift/SIL/TypeLowering.h" @@ -220,21 +221,34 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc, } } +// Finds stored properties that have the same type as `cd` and thus form +// a recursive structure. +// +// Example: +// +// class Node { +// let element: T +// let next: Node? +// } +// +// In the above example `next` is a recursive link and would be recognized +// by this function and added to the result set. void findRecursiveLinks(ClassDecl *cd, llvm::SmallSetVector &result) { - auto SelfTy = cd->getDeclaredInterfaceType(); + auto selfTy = cd->getDeclaredInterfaceType(); // Collect all stored properties that would form a recursive structure, // so we can remove the recursion and prevent the call stack from // overflowing. for (VarDecl *vd : cd->getStoredProperties()) { auto Ty = vd->getInterfaceType()->getOptionalObjectType(); - if (Ty && Ty->getCanonicalType() == SelfTy->getCanonicalType()) { + if (Ty && Ty->getCanonicalType() == selfTy->getCanonicalType()) { result.insert(vd); } } - // NOTE: Right now we only optimize linear recursion, so if there is more than - // one link, clear out the set and don't perform any recursion optimization. + // NOTE: Right now we only optimize linear recursion, so if there is more + // than one stored property of the same type, clear out the set and don't + // perform any recursion optimization. if (result.size() > 1) { result.clear(); } @@ -244,9 +258,9 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, ClassDecl *cd, VarDecl *recursiveLink, CleanupLocation cleanupLoc) { - auto SelfTy = F.mapTypeIntoContext(cd->getDeclaredInterfaceType()); + auto selfTy = F.mapTypeIntoContext(cd->getDeclaredInterfaceType()); - auto SelfTyLowered = getTypeLowering(SelfTy).getLoweredType(); + auto selfTyLowered = getTypeLowering(selfTy).getLoweredType(); SILBasicBlock *cleanBB = createBasicBlock(); SILBasicBlock *noneBB = createBasicBlock(); @@ -262,7 +276,7 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, SILValue varAddr = B.createRefElementAddr(cleanupLoc, selfValue.getValue(), recursiveLink, Ty.getAddressType()); - auto iterAddr = B.createAllocStack(cleanupLoc, Ty); + auto *iterAddr = B.createAllocStack(cleanupLoc, Ty); SILValue addr = B.createBeginAccess( cleanupLoc, varAddr, SILAccessKind::Modify, SILAccessEnforcement::Static, true /*noNestedConflict*/, false /*fromBuiltin*/); @@ -274,57 +288,77 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, B.createBranch(cleanupLoc, loopBB); // while iter != nil { - B.emitBlock(loopBB); - B.createSwitchEnumAddr( - cleanupLoc, iterAddr, nullptr, - {{getASTContext().getOptionalSomeDecl(), someBB}, - {std::make_pair(getASTContext().getOptionalNoneDecl(), noneBB)}}); + { + B.emitBlock(loopBB); + auto iterBorrow = + ManagedValue::forUnmanaged(iterAddr).borrow(*this, cleanupLoc); + SwitchEnumBuilder switchBuilder(B, cleanupLoc, iterBorrow); + switchBuilder.addOptionalSomeCase(someBB); + switchBuilder.addOptionalNoneCase(noneBB); + std::move(switchBuilder).emit(); + } // if isKnownUniquelyReferenced(&iter) { - B.emitBlock(someBB); - auto isUnique = B.createIsUnique(cleanupLoc, iterAddr); - B.createCondBranch(cleanupLoc, isUnique, uniqueBB, notUniqueBB); + { + B.emitBlock(someBB); + auto isUnique = B.createIsUnique(cleanupLoc, iterAddr); + B.createCondBranch(cleanupLoc, isUnique, uniqueBB, notUniqueBB); + } // we have a uniquely referenced link, so we need to deinit - B.emitBlock(uniqueBB); - - // NOTE: We increment the ref count of the tail instead of unlinking it, - // because custom deinit implementations of subclasses may access - // it and it would be semantically wrong to unset it before that. - // Making the tail non-uniquely referenced prevents the recursion. - - // let tail = iter.unsafelyUnwrapped.next - // iter = tail - SILValue iterBorrow = B.createLoadBorrow(cleanupLoc, iterAddr); - auto *link = B.createUncheckedEnumData(cleanupLoc, iterBorrow, - getASTContext().getOptionalSomeDecl(), - SelfTyLowered); - - varAddr = B.createRefElementAddr(cleanupLoc, link, recursiveLink, - Ty.getAddressType()); - - addr = B.createBeginAccess( - cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static, - true /* noNestedConflict */, false /*fromBuiltin*/); - iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); - B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); - B.createEndBorrow(cleanupLoc, iterBorrow); + { + B.emitBlock(uniqueBB); - B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign); + // NOTE: We increment the ref count of the tail instead of unlinking it, + // because custom deinit implementations of subclasses may access + // it and it would be semantically wrong to unset it before that. + // Making the tail non-uniquely referenced prevents the recursion. - B.createBranch(cleanupLoc, loopBB); + // let tail = iter.unsafelyUnwrapped.next + // iter = tail + SILValue iterBorrow = B.createLoadBorrow(cleanupLoc, iterAddr); + auto *link = B.createUncheckedEnumData( + cleanupLoc, iterBorrow, getASTContext().getOptionalSomeDecl(), + selfTyLowered); + + varAddr = B.createRefElementAddr(cleanupLoc, link, recursiveLink, + Ty.getAddressType()); + + addr = B.createBeginAccess( + cleanupLoc, varAddr, SILAccessKind::Read, SILAccessEnforcement::Static, + true /* noNestedConflict */, false /*fromBuiltin*/); + + // The deinit of `iter` will decrement the ref count of the field + // containing the next element and potentially leading to its + // deinitialization, causing the recursion. The prevent that, + // we `load [copy]` here to ensure the object stays alive until + // we explicitly release it in the next step of the iteration. + iter = B.createLoad(cleanupLoc, addr, LoadOwnershipQualifier::Copy); + B.createEndAccess(cleanupLoc, addr, false /*is aborting*/); + B.createEndBorrow(cleanupLoc, iterBorrow); + + B.createStore(cleanupLoc, iter, iterAddr, StoreOwnershipQualifier::Assign); + + B.createBranch(cleanupLoc, loopBB); + } // the next link in the chain is not unique, so we are done here - B.emitBlock(notUniqueBB); - B.createBranch(cleanupLoc, cleanBB); + { + B.emitBlock(notUniqueBB); + B.createBranch(cleanupLoc, cleanBB); + } // we reached the end of the chain - B.emitBlock(noneBB); - B.createBranch(cleanupLoc, cleanBB); + { + B.emitBlock(noneBB); + B.createBranch(cleanupLoc, cleanBB); + } - B.emitBlock(cleanBB); - B.createDestroyAddr(cleanupLoc, iterAddr); - B.createDeallocStack(cleanupLoc, iterAddr); + { + B.emitBlock(cleanBB); + B.createDestroyAddr(cleanupLoc, iterAddr); + B.createDeallocStack(cleanupLoc, iterAddr); + } } void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, @@ -355,6 +389,10 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, finishBB); } + // Before we destroy all fields, we check if any of them are + // recursively the same type as `self`, so we can iteratively + // deinitialize them, to prevent deep recursion and potential + // stack overflows. llvm::SmallSetVector recursiveLinks; findRecursiveLinks(cd, recursiveLinks); diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index e8493f2b3a5cd..3d183afbb6b22 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -683,6 +683,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Generates code to destroy linearly recursive data structures, without /// building up the call stack. /// + /// Example: + /// + /// class Node { + /// let value: A + /// let next: Node? + /// } + /// /// \param selfValue The 'self' value. /// \param cd The class declaration whose members are being destroyed. /// \param recursiveLink The property that forms the recursive structure. diff --git a/test/SILGen/deinit_recursive_no_overflow.swift b/test/Interpreter/deinit_recursive_no_overflow.swift similarity index 60% rename from test/SILGen/deinit_recursive_no_overflow.swift rename to test/Interpreter/deinit_recursive_no_overflow.swift index d3de82c35008d..2e50cfbac3ed8 100644 --- a/test/SILGen/deinit_recursive_no_overflow.swift +++ b/test/Interpreter/deinit_recursive_no_overflow.swift @@ -1,7 +1,4 @@ -// RUN: %empty-directory(%t) -// RUN: %target-build-swift %s -o %t/a.out -// RUN: %target-codesign %t/a.out -// RUN: %target-run %t/a.out +// RUN: %target-run-simple-swift // REQUIRES: executable_test @@ -20,4 +17,4 @@ for _ in 1...3_000_000 { func deallocList() { first = nil } -deallocList() +deallocList() \ No newline at end of file diff --git a/test/SILGen/deinit_recursive_linear.swift b/test/SILGen/deinit_recursive_linear.swift index b36d9b5760783..2ade3cac3d278 100644 --- a/test/SILGen/deinit_recursive_linear.swift +++ b/test/SILGen/deinit_recursive_linear.swift @@ -23,9 +23,11 @@ class Node { // CHECK: br [[LOOPBB:bb.*]] // // CHECK: [[LOOPBB]]: -// CHECK: switch_enum_addr [[ITER]] : $*Optional, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] +// CHECK: [[ITER_COPY:%.*]] = load [copy] [[ITER]] : $*Optional +// CHECK: switch_enum [[ITER_COPY]] : $Optional, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] -// CHECK: [[IS_SOME_BB]]: +// CHECK: [[IS_SOME_BB]]([[NODE:%.*]] : @owned $Node): +// CHECK: destroy_value [[NODE]] : $Node // CHECK: [[IS_UNIQUE:%.*]] = is_unique [[ITER]] : $*Optional // CHECK: cond_br [[IS_UNIQUE]], [[IS_UNIQUE_BB:bb.*]], [[NOT_UNIQUE_BB:bb[0-9]*]] diff --git a/test/SILGen/deinit_recursive_linear_generic.swift b/test/SILGen/deinit_recursive_linear_generic.swift index cd0b993cdb2dc..b6a734cdf3465 100644 --- a/test/SILGen/deinit_recursive_linear_generic.swift +++ b/test/SILGen/deinit_recursive_linear_generic.swift @@ -18,9 +18,11 @@ class Node { // CHECK: br [[LOOPBB:bb.*]] // // CHECK: [[LOOPBB]]: -// CHECK: switch_enum_addr [[ITER]] : $*Optional>, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] +// CHECK: [[ITER_COPY:%.*]] = load [copy] [[ITER]] : $*Optional> +// CHECK: switch_enum [[ITER_COPY]] : $Optional>, case #Optional.some!enumelt: [[IS_SOME_BB:bb.*]], case #Optional.none!enumelt: [[IS_NONE_BB:bb[0-9]+]] -// CHECK: [[IS_SOME_BB]]: +// CHECK: [[IS_SOME_BB]]([[NODE:%.*]] : @owned $Node): +// CHECK: destroy_value [[NODE]] : $Node // CHECK: [[IS_UNIQUE:%.*]] = is_unique [[ITER]] : $*Optional> // CHECK: cond_br [[IS_UNIQUE]], [[IS_UNIQUE_BB:bb.*]], [[NOT_UNIQUE_BB:bb[0-9]*]] From 5c56c600cfd624d4327c6d45734b4a501dfc94c0 Mon Sep 17 00:00:00 2001 From: stevapple Date: Sat, 5 Mar 2022 17:24:36 +0800 Subject: [PATCH 012/242] Add `.build` to gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index e48f694d57b1d..3f36a481f15ee 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,9 @@ docs/_build .cache .clangd +# SwiftPM +.build + #==============================================================================# # Ignore CMake temporary files #==============================================================================# @@ -59,6 +62,7 @@ CMakeFiles #==============================================================================# compile_commands.json +#==============================================================================# # Ignore generated GYB files until we fix the workaround on Windows #==============================================================================# 8 From 9d5ee7a081768410c117c48b21d774626a61bb49 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 7 Mar 2022 10:51:38 -0800 Subject: [PATCH 013/242] [ConstraintLocator] Add a new element - pattern binding element The new element points to an index of a particular pattern in a pattern binding declaration. --- include/swift/Sema/ConstraintLocator.h | 13 +++++++++++++ include/swift/Sema/ConstraintLocatorPathElts.def | 3 +++ lib/Sema/ConstraintLocator.cpp | 9 +++++++++ 3 files changed, 25 insertions(+) diff --git a/include/swift/Sema/ConstraintLocator.h b/include/swift/Sema/ConstraintLocator.h index d5996d53aea09..5c9adad004409 100644 --- a/include/swift/Sema/ConstraintLocator.h +++ b/include/swift/Sema/ConstraintLocator.h @@ -1058,6 +1058,19 @@ class LocatorPathElt::ClosureBodyElement final } }; +class LocatorPathElt::PatternBindingElement final + : public StoredIntegerElement<1> { +public: + PatternBindingElement(unsigned index) + : StoredIntegerElement(ConstraintLocator::PatternBindingElement, index) {} + + unsigned getIndex() const { return getValue(); } + + static bool classof(const LocatorPathElt *elt) { + return elt->getKind() == ConstraintLocator::PatternBindingElement; + } +}; + namespace details { template class PathElement { diff --git a/include/swift/Sema/ConstraintLocatorPathElts.def b/include/swift/Sema/ConstraintLocatorPathElts.def index 35c819d0035d8..0d9d8b4f5ceaa 100644 --- a/include/swift/Sema/ConstraintLocatorPathElts.def +++ b/include/swift/Sema/ConstraintLocatorPathElts.def @@ -232,6 +232,9 @@ SIMPLE_LOCATOR_PATH_ELT(ImplicitDynamicMemberSubscript) /// The element of the closure body e.g. statement, declaration, or expression. CUSTOM_LOCATOR_PATH_ELT(ClosureBodyElement) +/// The element of the pattern binding declaration. +CUSTOM_LOCATOR_PATH_ELT(PatternBindingElement) + #undef LOCATOR_PATH_ELT #undef CUSTOM_LOCATOR_PATH_ELT #undef SIMPLE_LOCATOR_PATH_ELT diff --git a/lib/Sema/ConstraintLocator.cpp b/lib/Sema/ConstraintLocator.cpp index 215cee2c1da58..c5d8de2184214 100644 --- a/lib/Sema/ConstraintLocator.cpp +++ b/lib/Sema/ConstraintLocator.cpp @@ -96,6 +96,7 @@ unsigned LocatorPathElt::getNewSummaryFlags() const { case ConstraintLocator::ClosureBodyElement: case ConstraintLocator::PackType: case ConstraintLocator::PackElement: + case ConstraintLocator::PatternBindingElement: return 0; case ConstraintLocator::FunctionArgument: @@ -578,6 +579,14 @@ void ConstraintLocator::dump(SourceManager *sm, raw_ostream &out) const { out << "pack element #" << llvm::utostr(packElt.getIndex()); break; } + + case PatternBindingElement: { + auto patternBindingElt = + elt.castTo(); + out << "pattern binding element #" + << llvm::utostr(patternBindingElt.getIndex()); + break; + } } } out << ']'; From 966f58f0440585604031a2ac804bc8bae62c4b75 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 7 Mar 2022 18:20:03 -0800 Subject: [PATCH 014/242] [Tests] NFC: Adjust all the test-cases improved by multi-statement inference --- test/Constraints/closures.swift | 17 +++++++------- test/Constraints/diagnostics.swift | 4 ++-- test/Constraints/members.swift | 14 +++++++++--- test/Constraints/patterns.swift | 4 ++-- test/Constraints/rdar46544601.swift | 2 +- test/Constraints/rdar65320500.swift | 8 +++---- test/Constraints/result_builder_diags.swift | 21 +++--------------- test/Constraints/tuple.swift | 6 ++--- test/Constraints/tuple_arguments.swift | 22 ++++++++++++++++--- .../without_actually_escaping.swift | 2 +- test/Sema/diag_ambiguous_overloads.swift | 16 ++++++-------- .../pretty-printed-diagnostics.swift | 13 ----------- test/expr/closure/anonymous.swift | 4 ---- test/expr/closure/closures.swift | 12 +++++----- test/expr/closure/inference.swift | 7 +++--- test/expr/closure/let.swift | 4 ++-- test/expr/expressions.swift | 4 +--- test/expr/unary/keypath/keypath.swift | 2 -- test/stmt/statements.swift | 9 +++++++- .../0119-rdar33613329.swift | 5 ++--- 20 files changed, 82 insertions(+), 94 deletions(-) diff --git a/test/Constraints/closures.swift b/test/Constraints/closures.swift index 42616c02e36bc..cb4a32e5aa3e4 100644 --- a/test/Constraints/closures.swift +++ b/test/Constraints/closures.swift @@ -252,7 +252,7 @@ struct CC {} func callCC(_ f: (CC) -> U) -> () {} func typeCheckMultiStmtClosureCrash() { - callCC { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}} + callCC { _ = $0 return 1 } @@ -312,9 +312,8 @@ func testAcceptNothingToInt(ac1: @autoclosure () -> Int) { struct Thing { init?() {} } -// This throws a compiler error -let things = Thing().map { thing in // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{34-34=-> <#Result#> }} - // Commenting out this makes it compile + +let things = Thing().map { thing in _ = thing return thing } @@ -322,14 +321,14 @@ let things = Thing().map { thing in // expected-error {{cannot infer return typ // QoI: [Closure return type inference] Swift cannot find members for the result of inlined lambdas with branches func r21675896(file : String) { - let x: String = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{20-20= () -> <#Result#> in }} + let x: String = { if true { return "foo" } else { return file } - }().pathExtension + }().pathExtension // expected-error {{value of type 'String' has no member 'pathExtension'}} } @@ -360,7 +359,7 @@ func someGeneric19997471(_ x: T) { // Swift fails to compile: [0].map() { _ in let r = (1,2).0; return r } -[0].map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{5-5=-> <#Result#> }} +let _ = [0].map { _ in let r = (1,2).0 return r @@ -408,7 +407,7 @@ func r20789423() { print(p.f(p)()) // expected-error {{cannot convert value of type 'C' to expected argument type 'Int'}} // expected-error@-1:11 {{cannot call value of non-function type '()'}} - let _f = { (v: Int) in // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{23-23=-> <#Result#> }} + let _f = { (v: Int) in print("a") return "hi" } @@ -1127,7 +1126,7 @@ func rdar76058892() { func experiment(arr: [S]?) { test { // expected-error {{contextual closure type '() -> String' expects 0 arguments, but 1 was used in closure body}} if let arr = arr { - arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}} + arr.map($0.test) // expected-note {{anonymous closure parameter '$0' is used here}} // expected-error {{generic parameter 'T' could not be inferred}} } } } diff --git a/test/Constraints/diagnostics.swift b/test/Constraints/diagnostics.swift index 6b7aa27c3f2f7..2ebd3e52c8a1d 100644 --- a/test/Constraints/diagnostics.swift +++ b/test/Constraints/diagnostics.swift @@ -148,7 +148,7 @@ func ***~(_: Int, _: String) { } i ***~ i // expected-error{{cannot convert value of type 'Int' to expected argument type 'String'}} @available(*, unavailable, message: "call the 'map()' method on the sequence") -public func myMap( +public func myMap( // expected-note {{'myMap' has been explicitly marked unavailable here}} _ source: C, _ transform: (C.Iterator.Element) -> T ) -> [T] { fatalError("unavailable function can't be called") @@ -161,7 +161,7 @@ public func myMap(_ x: T?, _ f: (T) -> U) -> U? { // func rdar20142523() { - myMap(0..<10, { x in // expected-error{{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{21-21=-> <#Result#> }} {{educational-notes=complex-closure-inference}} + _ = myMap(0..<10, { x in // expected-error {{'myMap' is unavailable: call the 'map()' method on the sequence}} () return x }) diff --git a/test/Constraints/members.swift b/test/Constraints/members.swift index 61e58d891c9b5..5dda70ee0d26b 100644 --- a/test/Constraints/members.swift +++ b/test/Constraints/members.swift @@ -595,10 +595,10 @@ func rdar50679161() { func foo() { _ = { () -> Void in + // Missing `.self` or `init` is not diagnosed here because there are errors in + // `if let` statement and `MiscDiagnostics` only run if the body is completely valid. var foo = S - // expected-error@-1 {{expected member name or constructor call after type name}} - // expected-note@-2 {{add arguments after the type to construct a value of the type}} - // expected-note@-3 {{use '.self' to reference the type object}} + if let v = Int?(1) { var _ = Q( a: v + foo.w, @@ -610,6 +610,14 @@ func rdar50679161() { ) } } + + _ = { () -> Void in + var foo = S + // expected-error@-1 {{expected member name or constructor call after type name}} + // expected-note@-2 {{add arguments after the type to construct a value of the type}} + // expected-note@-3 {{use '.self' to reference the type object}} + print(foo) + } } } diff --git a/test/Constraints/patterns.swift b/test/Constraints/patterns.swift index 40577ded3e629..0dd5c677991e2 100644 --- a/test/Constraints/patterns.swift +++ b/test/Constraints/patterns.swift @@ -230,14 +230,14 @@ func good(_ a: A) -> Int { } func bad(_ a: A) { - a.map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}} + let _ = a.map { let _: EE = $0 return 1 } } func ugly(_ a: A) { - a.map { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{none}} + let _ = a.map { switch $0 { case .A: return 1 diff --git a/test/Constraints/rdar46544601.swift b/test/Constraints/rdar46544601.swift index e633bda722ee2..28e5882d6772e 100644 --- a/test/Constraints/rdar46544601.swift +++ b/test/Constraints/rdar46544601.swift @@ -26,6 +26,6 @@ func crash(_ p: P, payload: [UInt8]) throws { p.foo(arr: arr, data: []).and(result: (id, arr)) }.then { args0 in let (parentID, args1) = args0 - p.bar(root: parentID, from: p).and(args1) + p.bar(root: parentID, from: p).and(result: args1) }.whenFailure { _ in } } diff --git a/test/Constraints/rdar65320500.swift b/test/Constraints/rdar65320500.swift index 25b08ac7aad5e..2d9dc9f9311a4 100644 --- a/test/Constraints/rdar65320500.swift +++ b/test/Constraints/rdar65320500.swift @@ -31,13 +31,13 @@ test_builder { test_builder { test(doesntExist()) // expected-error {{cannot find 'doesntExist' in scope}} - if let result = doesntExist() { + if let result = doesntExist() { // expected-error {{cannot find 'doesntExist' in scope}} } - if bar = test(42) {} + if bar = test(42) {} // expected-error {{cannot find 'bar' in scope}} - let foo = bar() + let foo = bar() // expected-error {{cannot find 'bar' in scope}} - switch (doesntExist()) { + switch (doesntExist()) { // expected-error {{cannot find 'doesntExist' in scope}} } } diff --git a/test/Constraints/result_builder_diags.swift b/test/Constraints/result_builder_diags.swift index ce189a976d472..8f91ff8294032 100644 --- a/test/Constraints/result_builder_diags.swift +++ b/test/Constraints/result_builder_diags.swift @@ -78,7 +78,7 @@ struct TupleBuilderWithoutIf { // expected-note 3{{struct 'TupleBuilderWithoutIf static func buildDo(_ value: T) -> T { return value } } -func tuplify(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { +func tuplify(_ cond: Bool, @TupleBuilder body: (Bool) -> T) { // expected-note {{in call to function 'tuplify(_:body:)'}} print(body(cond)) } @@ -307,21 +307,6 @@ struct MyTuplifiedStruct { } } -func test_invalid_return_type_in_body() { - tuplify(true) { _ -> (Void, Int) in - tuplify(false) { condition in - if condition { - return 42 // expected-error {{cannot use explicit 'return' statement in the body of result builder 'TupleBuilder'}} - // expected-note@-1 {{remove 'return' statements to apply the result builder}} {{9-16=}} - } else { - 1 - } - } - - 42 - } -} - // Check that we're performing syntactic use diagnostics. func acceptMetatype(_: T.Type) -> Bool { true } @@ -481,7 +466,7 @@ struct TestConstraintGenerationErrors { func buildTupleClosure() { tuplify(true) { _ in let a = nothing // expected-error {{cannot find 'nothing' in scope}} - String(nothing) + String(nothing) // expected-error {{cannot find 'nothing' in scope}} } } } @@ -522,7 +507,7 @@ enum E3 { } func testCaseMutabilityMismatches(e: E3) { - tuplify(true) { c in + tuplify(true) { c in // expected-error {{generic parameter 'T' could not be inferred}} "testSwitch" switch e { case .a(let x, var y), diff --git a/test/Constraints/tuple.swift b/test/Constraints/tuple.swift index d89ee2b5d1251..5c2965e6b4b2b 100644 --- a/test/Constraints/tuple.swift +++ b/test/Constraints/tuple.swift @@ -218,14 +218,14 @@ extension r25271859 { func map(f: (T) -> U) -> r25271859 { } - func andThen(f: (T) -> r25271859) { // expected-note {{in call to function 'andThen(f:)'}} + func andThen(f: (T) -> r25271859) { } } func f(a : r25271859<(Float, Int)>) { - a.map { $0.0 } // expected-error {{generic parameter 'U' could not be inferred}} (This is related to how solver is setup with multiple statements) + a.map { $0.0 } .andThen { _ in - print("hello") // comment this out and it runs, leave any form of print in and it doesn't + print("hello") return r25271859() } } diff --git a/test/Constraints/tuple_arguments.swift b/test/Constraints/tuple_arguments.swift index ec14a257c96aa..a568be0bc5206 100644 --- a/test/Constraints/tuple_arguments.swift +++ b/test/Constraints/tuple_arguments.swift @@ -1407,25 +1407,41 @@ func processArrayOfFunctions(f1: [((Bool, Bool)) -> ()], } f2.forEach { block in - // expected-note@-1 2{{'block' declared here}} + // expected-note@-1 {{'block' declared here}} block(p) // expected-error {{parameter 'block' expects 2 separate arguments}} + } + + f2.forEach { block in + // expected-note@-1 {{'block' declared here}} block((c, c)) // expected-error {{parameter 'block' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{11-12=}} {{16-17=}} block(c, c) } + f2.forEach { block in + block(c, c) + } + f2.forEach { (block: ((Bool, Bool)) -> ()) in // expected-error@-1 {{cannot convert value of type '(((Bool, Bool)) -> ()) -> Void' to expected argument type '(@escaping (Bool, Bool) -> ()) throws -> Void'}} block(p) block((c, c)) - block(c, c) + block(c, c) // expected-error {{parameter 'block' expects a single parameter of type '(Bool, Bool)'}} } f2.forEach { (block: (Bool, Bool) -> ()) in - // expected-note@-1 2{{'block' declared here}} + // expected-note@-1 {{'block' declared here}} block(p) // expected-error {{parameter 'block' expects 2 separate arguments}} + } + + f2.forEach { (block: (Bool, Bool) -> ()) in + // expected-note@-1 {{'block' declared here}} block((c, c)) // expected-error {{parameter 'block' expects 2 separate arguments; remove extra parentheses to change tuple into separate arguments}} {{11-12=}} {{16-17=}} block(c, c) } + + f2.forEach { (block: (Bool, Bool) -> ()) in + block(c, c) + } } // expected-error@+1 {{cannot create a single-element tuple with an element label}} diff --git a/test/Constraints/without_actually_escaping.swift b/test/Constraints/without_actually_escaping.swift index 12752c08e8c2e..d6ce858c24801 100644 --- a/test/Constraints/without_actually_escaping.swift +++ b/test/Constraints/without_actually_escaping.swift @@ -10,7 +10,7 @@ func escapeX(_ xx: (Int) -> Int, _ value: Int) { // expected-note* {{non-escapin withoutActuallyEscaping(xx) { escapableXX in x = xx // expected-error{{non-escaping parameter}} x = escapableXX - x = xx // expected-error{{non-escaping parameter}} + x = xx _ = x(value) _ = xx(value) diff --git a/test/Sema/diag_ambiguous_overloads.swift b/test/Sema/diag_ambiguous_overloads.swift index 1aa56b1b0bece..e351f3885d935 100644 --- a/test/Sema/diag_ambiguous_overloads.swift +++ b/test/Sema/diag_ambiguous_overloads.swift @@ -132,15 +132,13 @@ func SR12689(_ u: UnsafeBufferPointer) {} let array : [UInt16] = [1, 2] array.withUnsafeBufferPointer { - SR12689(UnsafeRawPointer($0).bindMemory(to: UInt16.self, capacity: 1)) // expected-error {{cannot convert value of type 'UnsafePointer' to expected argument type 'UnsafeBufferPointer'}} - // expected-error@-1 {{no exact matches in call to initializer}} - // expected-note@-2 {{candidate expects value of type 'UnsafeRawPointer' for parameter #1}} - // expected-note@-3 {{candidate expects value of type 'UnsafeMutableRawPointer' for parameter #1}} - - UnsafeRawPointer($0) as UnsafeBufferPointer // expected-error {{cannot convert value of type 'UnsafeRawPointer' to type 'UnsafeBufferPointer' in coercion}} - // expected-error@-1 {{no exact matches in call to initializer}} - // expected-note@-2 {{found candidate with type '(UnsafeRawPointer) -> UnsafeRawPointer'}} - // expected-note@-3 {{found candidate with type '(UnsafeMutableRawPointer) -> UnsafeRawPointer'}} + _ = SR12689(UnsafeRawPointer($0).bindMemory(to: UInt16.self, capacity: 1)) // expected-error {{cannot convert value of type 'UnsafePointer' to expected argument type 'UnsafeBufferPointer'}} + // expected-error@-1 {{cannot convert value of type 'UnsafeBufferPointer' to expected argument type 'UnsafeMutableRawPointer'}} +} + +array.withUnsafeBufferPointer { + _ = UnsafeRawPointer($0) as UnsafeBufferPointer // expected-error {{cannot convert value of type 'UnsafeRawPointer' to type 'UnsafeBufferPointer' in coercion}} + // expected-error@-1 {{cannot convert value of type 'UnsafeBufferPointer' to expected argument type 'UnsafeMutableRawPointer'}} } func SR12689_1(_ u: Int) -> String { "" } // expected-note {{found this candidate}} expected-note {{candidate expects value of type 'Int' for parameter #1 (got 'Double')}} diff --git a/test/diagnostics/pretty-printed-diagnostics.swift b/test/diagnostics/pretty-printed-diagnostics.swift index b110ecfa45c23..6ff1e65045cd1 100644 --- a/test/diagnostics/pretty-printed-diagnostics.swift +++ b/test/diagnostics/pretty-printed-diagnostics.swift @@ -124,13 +124,6 @@ foo(b: // CHECK: | ^ note: Remove '=' to make 'x' a computed property [remove '= ' and replace 'let' with 'var'] // CHECK: [[#LINE+1]] | } -// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:9 -// CHECK: [[#LINE-1]] | -// CHECK: [[#LINE]] | let x = { () -> Result in -// CHECK: | +++++++++++++++++ -// CHECK: | ^ error: cannot infer return type for closure with multiple statements; add explicit type to disambiguate -// CHECK: [[#LINE+1]] | let y = 1 - // CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:8 // CHECK: [[#LINE-1]] | // CHECK: [[#LINE]] | struct B: Decodable { @@ -149,12 +142,6 @@ foo(b: // CHECK: | ^ error: argument 'a' must precede argument 'b' [remove ', a: 2' and insert 'a: 2, '] // CHECK: [[#LINE+1]] | -// CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:20 -// CHECK: [[#LINE-1]] | -// CHECK: [[#LINE]] | let 👍👍👍 = { -// CHECK: | --> error: cannot infer return type for closure with multiple statements; add explicit type to disambiguate [insert ' () -> <#Result#> in '] -// CHECK: [[#LINE+1]] | let y = 1 - // CHECK: SOURCE_DIR{{[/\]+}}test{{[/\]+}}diagnostics{{[/\]+}}pretty-printed-diagnostics.swift:[[#LINE:]]:5 // CHECK: [[#LINE-2]] | // Multi-line fix-its // CHECK: [[#LINE-1]] | foo(a: 2, b: 1, diff --git a/test/expr/closure/anonymous.swift b/test/expr/closure/anonymous.swift index 3a9caf47f7537..e5de36e8d2412 100644 --- a/test/expr/closure/anonymous.swift +++ b/test/expr/closure/anonymous.swift @@ -29,11 +29,7 @@ func variadic() { takesVariadicGeneric({takesIntArray($0)}) - // FIXME: Problem here is related to multi-statement closure body not being type-checked together with - // enclosing context. We could have inferred `$0` to be `[Int]` if `let` was a part of constraint system. takesVariadicGeneric({let _: [Int] = $0}) - // expected-error@-1 {{unable to infer type of a closure parameter '$0' in the current context}} - takesVariadicIntInt({_ = $0; takesIntArray($1)}) takesVariadicIntInt({_ = $0; let _: [Int] = $1}) } diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 49508cc7a8fd2..658f5f3586bf3 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -120,9 +120,8 @@ var selfRef = { selfRef() } // expected-note@-1 2{{through reference here}} // expected-error@-2 {{circular reference}} -var nestedSelfRef = { +var nestedSelfRef = { // expected-error {{circular reference}} expected-note 2 {{through reference here}} var recursive = { nestedSelfRef() } - // expected-warning@-1 {{variable 'recursive' was never mutated; consider changing to 'let' constant}} recursive() } @@ -140,12 +139,11 @@ func anonymousClosureArgsInClosureWithArgs() { var a3 = { (z: Int) in $0 } // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'z'?}} {{26-28=z}} var a4 = { (z: [Int], w: [Int]) in f($0.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'z'?}} {{7-9=z}} expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}} - f($1.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'w'?}} {{7-9=w}} expected-error {{cannot convert value of type 'Int' to expected argument type 'String'}} + f($1.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'w'?}} {{7-9=w}} } var a5 = { (_: [Int], w: [Int]) in f($0.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments}} f($1.count) // expected-error {{anonymous closure arguments cannot be used inside a closure that has explicit arguments; did you mean 'w'?}} {{7-9=w}} - // expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'String'}} } } @@ -403,7 +401,7 @@ Void(0) // expected-error{{argument passed to call that takes no arguments}} _ = {0} // "multi-statement closures require an explicit return type" should be an error not a note -let samples = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} +let samples = { if (i > 10) { return true } else { return false } }() @@ -485,8 +483,8 @@ func lvalueCapture(c: GenericClass) { } // Don't expose @lvalue-ness in diagnostics. -let closure = { // expected-error {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} {{16-16= () -> <#Result#> in }} - var helper = true +let closure = { + var helper = true // expected-warning {{variable 'helper' was never mutated; consider changing to 'let' constant}} return helper } diff --git a/test/expr/closure/inference.swift b/test/expr/closure/inference.swift index b1b536c53991f..0e9ac2a943f4b 100644 --- a/test/expr/closure/inference.swift +++ b/test/expr/closure/inference.swift @@ -34,10 +34,9 @@ func unnamed() { // Regression tests. var nestedClosuresWithBrokenInference = { f: Int in {} } - // expected-error@-1 {{closure expression is unused}} expected-note@-1 {{did you mean to use a 'do' statement?}} {{53-53=do }} - // expected-error@-2 {{consecutive statements on a line must be separated by ';'}} {{44-44=;}} - // expected-error@-3 {{expected expression}} - // expected-error@-4 {{cannot find 'f' in scope}} + // expected-error@-1 {{consecutive statements on a line must be separated by ';'}} {{44-44=;}} + // expected-error@-2 {{expected expression}} + // expected-error@-3 {{cannot find 'f' in scope}} // SR-11540 diff --git a/test/expr/closure/let.swift b/test/expr/closure/let.swift index 176629f7c9517..4d4bfdfd86846 100644 --- a/test/expr/closure/let.swift +++ b/test/expr/closure/let.swift @@ -9,11 +9,11 @@ func foo() { _ = { frob(x: x) }() // expected-error{{'x' is a 'let'}} _ = { x = 0 }() // expected-error{{'x' is a 'let'}} - _ = { frob(x: x); x = 0 }() // expected-error 2 {{'x' is a 'let'}} + _ = { frob(x: x); x = 0 }() // expected-error {{'x' is a 'let'}} } let a: Int { 1 } // expected-error{{'let' declarations cannot be computed properties}} let b: Int = 1 -{ didSet { print("didSet") } } // expected-error{{'let' declarations cannot be observing properties}} \ No newline at end of file +{ didSet { print("didSet") } } // expected-error{{'let' declarations cannot be observing properties}} diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index 78a5897cbbfce..1c2bb47a4ee4d 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -246,11 +246,9 @@ func test_as_2() { func test_lambda() { // A simple closure. var a = { (value: Int) -> () in markUsed(value+1) } - // expected-warning@-1 {{initialization of variable 'a' was never used; consider replacing with assignment to '_' or removing it}} // A recursive lambda. - var fib = { (n: Int) -> Int in - // expected-warning@-1 {{variable 'fib' was never mutated; consider changing to 'let' constant}} + var fib = { (n: Int) -> Int in // expected-error {{circular reference}} expected-note 2 {{through reference here}} if (n < 2) { return n } diff --git a/test/expr/unary/keypath/keypath.swift b/test/expr/unary/keypath/keypath.swift index f50823e037828..ab9f6ea21afae 100644 --- a/test/expr/unary/keypath/keypath.swift +++ b/test/expr/unary/keypath/keypath.swift @@ -1093,8 +1093,6 @@ func rdar74711236() { // `isSupported` should be an invalid declaration to trigger a crash in `map(\.option)` let isSupported = context!.supported().contains(type) return (isSupported ? [type] : []).map(\.option) - // expected-error@-1 {{value of type 'Any' has no member 'option'}} - // expected-note@-2 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}} } return [] }() diff --git a/test/stmt/statements.swift b/test/stmt/statements.swift index 1f05959eb4448..c3348cce6ffc9 100644 --- a/test/stmt/statements.swift +++ b/test/stmt/statements.swift @@ -400,13 +400,20 @@ func test_is_as_patterns() { } // Fuzzing SourceKit: crash in Parser::parseStmtForEach(...) -func matching_pattern_recursion() { +func matching_pattern_recursion(zs: [Int]) { // expected-note {{'zs' declared here}} switch 42 { case { // expected-error {{expression pattern of type '() -> ()' cannot match values of type 'Int'}} for i in zs { } }: break } + + switch 42 { + case { + for i in ws { // expected-error {{cannot find 'ws' in scope; did you mean 'zs'?}} + } + }: break + } } // Swift's break operator in switch should be indicated in errors diff --git a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift index 176928ec6781b..36d9906941ced 100644 --- a/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift +++ b/validation-test/compiler_crashers_2_fixed/0119-rdar33613329.swift @@ -17,7 +17,7 @@ struct M { } } -protocol P { // expected-note {{where 'Self' = 'M, R>'}} +protocol P { // expected-note {{where 'Self' = 'M, Int>'}} associatedtype A associatedtype B @@ -42,5 +42,4 @@ extension WritableKeyPath : P { struct X { var y: Int = 0 } var x = X() x ~> \X.y ≈> { a in a += 1; return 3 } -// expected-error@-1 {{referencing operator function '~>' on 'P' requires that 'M, R>' conform to 'P'}} -// expected-error@-2 {{cannot infer return type for closure with multiple statements; add explicit type to disambiguate}} +//expected-error@-1 {{referencing operator function '~>' on 'P' requires that 'M, Int>' conform to 'P'}} From 2028a030f560efbde77158d8ffa12a94c381258c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 3 Mar 2022 14:35:34 -0500 Subject: [PATCH 015/242] stdlib: Build with -requirement-machine-inferred-signatures=verify --- stdlib/cmake/modules/SwiftSource.cmake | 5 +++++ .../stability-stdlib-source-x86_64.swift.expected | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/stdlib/cmake/modules/SwiftSource.cmake b/stdlib/cmake/modules/SwiftSource.cmake index b221f1a41dab5..183cb10c95c45 100644 --- a/stdlib/cmake/modules/SwiftSource.cmake +++ b/stdlib/cmake/modules/SwiftSource.cmake @@ -465,6 +465,11 @@ function(_compile_swift_files endif() endif() + # The standard library and overlays are built with the Requirement Machine enabled. + if(SWIFTFILE_IS_STDLIB) + list(APPEND swift_flags "-Xfrontend" "-requirement-machine-inferred-signatures=verify") + endif() + # The standard library and overlays are built resiliently when SWIFT_STDLIB_STABLE_ABI=On. if(SWIFTFILE_IS_STDLIB AND SWIFT_STDLIB_STABLE_ABI) list(APPEND swift_flags "-enable-library-evolution") diff --git a/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected b/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected index 88481329a3301..b8d9b50625012 100644 --- a/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected +++ b/test/api-digester/Outputs/stability-stdlib-source-x86_64.swift.expected @@ -15,4 +15,14 @@ Func SignedInteger.&-(_:_:) has been removed Protocol Collection has generic signature change from to -Protocol StringProtocol has generic signature change from to \ No newline at end of file +Protocol StringProtocol has generic signature change from to + +// The RequirementMachine does not preserve sugared types in requirements. Not actually a source break. +Func Substring.UnicodeScalarView.replaceSubrange(_:with:) has generic signature change from to +Func Substring.replaceSubrange(_:with:) has generic signature change from to +Func Unicode.ASCII.Parser.parseScalar(from:) has generic signature change from to +Func Unicode.UTF16.decode(_:) has generic signature change from to +Func Unicode.UTF32.Parser.parseScalar(from:) has generic signature change from to +Func Unicode.UTF32.decode(_:) has generic signature change from to +Func Unicode.UTF8.decode(_:) has generic signature change from to +Constructor Mirror.init(_:children:displayStyle:ancestorRepresentation:) has generic signature change from to From a5cff5afb56d257f1a6820583b9d50e96c8c436f Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 8 Mar 2022 11:33:27 -0800 Subject: [PATCH 016/242] [CSGen] Always record a type of named pattern declaration This is going to be used by multi-statement closure inference because patterns could be declarated and referenced in the body via the associated declaration. --- lib/Sema/CSGen.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index 9d292b284a53b..6249f3bff491c 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2299,9 +2299,71 @@ namespace { locator); } - // If we have a type to ascribe to the variable, do so now. - if (oneWayVarType) + // Ascribe a type to the declaration so it's always available to + // constraint system. + if (oneWayVarType) { CS.setType(var, oneWayVarType); + } else if (externalPatternType) { + // If there is an externally imposed type, that's what the + // declaration is going to be bound to. + CS.setType(var, externalPatternType); + } else { + // Otherwise, let's use the type of the pattern. The type + // of the declaration has to be r-value, so let's add an + // equality constraint if pattern type has any type variables + // that are allowed to be l-value. + bool foundLValueVars = false; + + // Note that it wouldn't be always correct to allocate a single type + // variable, that disallows l-value types, to use as a declaration + // type because equality constraint would drop TVO_CanBindToLValue + // from the right-hand side (which is not the case for `OneWayEqual`) + // e.g.: + // + // sturct S { var x, y: Int } + // + // func test(s: S) { + // let (x, y) = (s.x, s.y) + // } + // + // Single type variable approach results in the following constraint: + // `$T_x_y = ($T_s_x, $T_s_y)` where both `$T_s_x` and `$T_s_y` have + // to allow l-value, but `$T_x_y` does not. Early simplication of `=` + // constraint (due to right-hand side being a "concrete" tuple type) + // would drop l-value option from `$T_s_x` and `$T_s_y` which leads to + // a failure during member lookup because `x` and `y` are both + // `@lvalue Int`. To avoid that, declaration type would mimic pattern + // type with all l-value options stripped, so the equality constraint + // becomes `($T_x, $_T_y) = ($T_s_x, $T_s_y)` which doesn't result in + // stripping of l-value flag from the right-hand side since + // simplification can only happen when either side is resolved. + auto declTy = varType.transform([&](Type type) -> Type { + if (auto *typeVar = type->getAs()) { + if (typeVar->getImpl().canBindToLValue()) { + foundLValueVars = true; + + // Drop l-value from the options but preserve the rest. + auto options = typeVar->getImpl().getRawOptions(); + options &= ~TVO_CanBindToLValue; + + return CS.createTypeVariable(typeVar->getImpl().getLocator(), + options); + } + } + return type; + }); + + // If pattern types allows l-value types, let's create an + // equality constraint between r-value only declaration type + // and l-value pattern type that would take care of looking + // through l-values when necessary. + if (foundLValueVars) { + CS.addConstraint(ConstraintKind::Equal, declTy, varType, + CS.getConstraintLocator(locator)); + } + + CS.setType(var, declTy); + } return setType(varType); } From ceb78c091c870d4f303214d3c66ba3f3b0caee4a Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 8 Mar 2022 11:35:42 -0800 Subject: [PATCH 017/242] [Constraint] NFC: Improve debug output of pattern binding element constraints --- lib/Sema/Constraint.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 1001ab23be28e..1dcef91a86354 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -388,8 +388,21 @@ void Constraint::print(llvm::raw_ostream &Out, SourceManager *sm) const { } if (Kind == ConstraintKind::ClosureBodyElement) { - Out << "closure body element "; - getClosureElement().dump(Out); + auto *locator = getLocator(); + auto element = getClosureElement(); + + if (auto patternBindingElt = + locator + ->getLastElementAs()) { + auto *patternBinding = cast(element.get()); + Out << "pattern binding element @ "; + Out << patternBindingElt->getIndex() << " : "; + patternBinding->getPattern(patternBindingElt->getIndex())->dump(Out); + } else { + Out << "closure body element "; + getClosureElement().dump(Out); + } + return; } From 5c3fb222e1397b7a0b939df6994ad9a15847f8c4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 8 Mar 2022 11:52:16 -0800 Subject: [PATCH 018/242] [CSClosure] Explode pattern binding declarations into conjunctions `one-way` constraints disable some optimizations related to component selection because they imply strict ordering. This is a problem for multi-statement closures because variable declarations could involve complex operator expressions that rely on aforementioned optimizations. In order to fix that, let's move away from solving whole pattern binding declaration into scheme that explodes such declarations into indvidual elements and inlines them into a conjunction. For example: ``` let x = 42, y = x + 1, z = (x, test()) ``` Would result in a conjunction of three elements: ``` x = 42 y = x + 1 z = (x, test()) ``` Each element is solved indepedently, which eliminates the need for `one-way` constraints and re-enables component selection optimizations. --- lib/Sema/CSClosure.cpp | 93 ++++++++++++++++++- test/expr/closure/closures.swift | 9 +- test/expr/expressions.swift | 4 +- ...statement_closure_with_simd_variable.swift | 18 ++++ 4 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index c94a9f0b614a3..13bee8a11518a 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -483,12 +483,81 @@ class ClosureConstraintGenerator }); } + void visitPatternBinding(PatternBindingDecl *patternBinding, + SmallVectorImpl &patterns) { + auto *baseLoc = cs.getConstraintLocator( + locator, LocatorPathElt::ClosureBodyElement(patternBinding)); + + for (unsigned index : range(patternBinding->getNumPatternEntries())) { + auto *pattern = TypeChecker::resolvePattern( + patternBinding->getPattern(index), patternBinding->getDeclContext(), + /*isStmtCondition=*/true); + + if (!pattern) { + hadError = true; + return; + } + + // Reset binding to point to the resolved pattern. This is required + // before calling `forPatternBindingDecl`. + patternBinding->setPattern(index, pattern, + patternBinding->getInitContext(index)); + + patterns.push_back(makeElement( + patternBinding, + cs.getConstraintLocator( + baseLoc, LocatorPathElt::PatternBindingElement(index)))); + } + } + + void visitPatternBindingElement(PatternBindingDecl *patternBinding) { + assert(locator->isLastElement()); + + auto index = + locator->castLastElementTo() + .getIndex(); + + auto contextualPattern = + ContextualPattern::forPatternBindingDecl(patternBinding, index); + Type patternType = TypeChecker::typeCheckPattern(contextualPattern); + + // Fail early if pattern couldn't be type-checked. + if (!patternType || patternType->hasError()) { + hadError = true; + return; + } + + auto *pattern = patternBinding->getPattern(index); + auto *init = patternBinding->getInit(index); + + if (!init && patternBinding->isDefaultInitializable(index) && + pattern->hasStorage()) { + init = TypeChecker::buildDefaultInitializer(patternType); + } + + auto target = init ? SolutionApplicationTarget::forInitialization( + init, patternBinding->getDeclContext(), + patternType, patternBinding, index, + /*bindPatternVarsOneWay=*/false) + : SolutionApplicationTarget::forUninitializedVar( + patternBinding, index, patternType); + + if (cs.generateConstraints(target, FreeTypeVariableBinding::Disallow)) { + hadError = true; + return; + } + + // Keep track of this binding entry. + cs.setSolutionApplicationTarget({patternBinding, index}, target); + } + void visitDecl(Decl *decl) { if (isSupportedMultiStatementClosure()) { if (auto patternBinding = dyn_cast(decl)) { - SolutionApplicationTarget target(patternBinding); - if (cs.generateConstraints(target, FreeTypeVariableBinding::Disallow)) - hadError = true; + if (locator->isLastElement()) + visitPatternBindingElement(patternBinding); + else + llvm_unreachable("cannot visit pattern binding directly"); return; } } @@ -788,6 +857,13 @@ class ClosureConstraintGenerator element.is() && (!ctx.LangOpts.Playground && !ctx.LangOpts.DebuggerSupport); + if (auto *decl = element.dyn_cast()) { + if (auto *PDB = dyn_cast(decl)) { + visitPatternBinding(PDB, elements); + continue; + } + } + elements.push_back(makeElement( element, cs.getConstraintLocator( @@ -1600,6 +1676,17 @@ void ConjunctionElement::findReferencedVariables( TypeVariableRefFinder refFinder(cs, locator->getAnchor(), typeVars); + if (auto *patternBinding = + dyn_cast_or_null(element.dyn_cast())) { + if (auto patternBindingElt = + locator + ->getLastElementAs()) { + if (auto *init = patternBinding->getInit(patternBindingElt->getIndex())) + init->walk(refFinder); + return; + } + } + if (element.is() || element.is() || element.is() || element.isStmt(StmtKind::Return)) element.walk(refFinder); diff --git a/test/expr/closure/closures.swift b/test/expr/closure/closures.swift index 658f5f3586bf3..0c2c821665539 100644 --- a/test/expr/closure/closures.swift +++ b/test/expr/closure/closures.swift @@ -115,13 +115,14 @@ func t() { func f0(_ a: Any) -> Int { return 1 } assert(f0(1) == 1) - +// TODO(diagnostics): Bad diagnostic - should be `circular reference` var selfRef = { selfRef() } -// expected-note@-1 2{{through reference here}} -// expected-error@-2 {{circular reference}} +// expected-error@-1 {{unable to infer closure type in the current context}} -var nestedSelfRef = { // expected-error {{circular reference}} expected-note 2 {{through reference here}} +// TODO: should be an error `circular reference` but it's diagnosed via overlapped requests +var nestedSelfRef = { var recursive = { nestedSelfRef() } + // expected-warning@-1 {{variable 'recursive' was never mutated; consider changing to 'let' constant}} recursive() } diff --git a/test/expr/expressions.swift b/test/expr/expressions.swift index 1c2bb47a4ee4d..78a5897cbbfce 100644 --- a/test/expr/expressions.swift +++ b/test/expr/expressions.swift @@ -246,9 +246,11 @@ func test_as_2() { func test_lambda() { // A simple closure. var a = { (value: Int) -> () in markUsed(value+1) } + // expected-warning@-1 {{initialization of variable 'a' was never used; consider replacing with assignment to '_' or removing it}} // A recursive lambda. - var fib = { (n: Int) -> Int in // expected-error {{circular reference}} expected-note 2 {{through reference here}} + var fib = { (n: Int) -> Int in + // expected-warning@-1 {{variable 'fib' was never mutated; consider changing to 'let' constant}} if (n < 2) { return n } diff --git a/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift b/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift new file mode 100644 index 0000000000000..294ff03cb814c --- /dev/null +++ b/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 +// REQUIRES: objc_interop,no_asan + +import simd + +func test(_: () -> Void) {} + +test { + let a: simd_float2 = .init() + let b: simd_float2 = .init() + let c: simd_float2 = .init() + let d: simd_float2 = .init() + + let width: Float = 2 + + let p = Int(max(20, min(1000, (simd_distance(a, b) + simd_distance(b, c) + simd_distance(c, d)) / width / 4))) + print(p) +} From 5e4c964c36d69fba5c1b172e9e8cea8c5da1f2b6 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 8 Mar 2022 12:02:15 -0800 Subject: [PATCH 019/242] [TypeChecker] SE-0326: Enable multi-statement closure inference by default --- include/swift/Basic/LangOptions.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 93ca008b25c25..d2144d14d3383 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -724,7 +724,7 @@ namespace swift { /// Enable experimental support for type inference through multi-statement /// closures. - bool EnableMultiStatementClosureInference = false; + bool EnableMultiStatementClosureInference = true; /// Enable experimental support for generic parameter inference in /// parameter positions from associated default expressions. From ba0de89a52181389142e564a8fe2ecc4f1f38fc9 Mon Sep 17 00:00:00 2001 From: Dario Rexin Date: Wed, 9 Mar 2022 13:56:47 -0800 Subject: [PATCH 020/242] Addressed more review feedback --- lib/SILGen/SILGenDestructor.cpp | 39 +++++++++++++++------------------ lib/SILGen/SILGenFunction.h | 2 +- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/lib/SILGen/SILGenDestructor.cpp b/lib/SILGen/SILGenDestructor.cpp index 9a76be26f1b29..169bb924d8a7c 100644 --- a/lib/SILGen/SILGenDestructor.cpp +++ b/lib/SILGen/SILGenDestructor.cpp @@ -221,19 +221,20 @@ void SILGenFunction::destroyClassMember(SILLocation cleanupLoc, } } -// Finds stored properties that have the same type as `cd` and thus form -// a recursive structure. -// -// Example: -// -// class Node { -// let element: T -// let next: Node? -// } -// -// In the above example `next` is a recursive link and would be recognized -// by this function and added to the result set. -void findRecursiveLinks(ClassDecl *cd, llvm::SmallSetVector &result) { +/// Finds stored properties that have the same type as `cd` and thus form +/// a recursive structure. +/// +/// Example: +/// +/// class Node { +/// let element: T +/// let next: Node? +/// } +/// +/// In the above example `next` is a recursive link and would be recognized +/// by this function and added to the result set. +static void findRecursiveLinks(ClassDecl *cd, + llvm::SmallSetVector &result) { auto selfTy = cd->getDeclaredInterfaceType(); // Collect all stored properties that would form a recursive structure, @@ -309,11 +310,6 @@ void SILGenFunction::emitRecursiveChainDestruction(ManagedValue selfValue, { B.emitBlock(uniqueBB); - // NOTE: We increment the ref count of the tail instead of unlinking it, - // because custom deinit implementations of subclasses may access - // it and it would be semantically wrong to unset it before that. - // Making the tail non-uniquely referenced prevents the recursion. - // let tail = iter.unsafelyUnwrapped.next // iter = tail SILValue iterBorrow = B.createLoadBorrow(cleanupLoc, iterAddr); @@ -373,10 +369,10 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, /// For other cases, the basic blocks are not necessary and the destructor /// can just emit all the normal destruction code right into the current block. // If set, used as the basic block for the destroying of all members. - SILBasicBlock* normalMemberDestroyBB = nullptr; + SILBasicBlock *normalMemberDestroyBB = nullptr; // If set, used as the basic block after members have been destroyed, // and we're ready to perform final cleanups before returning. - SILBasicBlock* finishBB = nullptr; + SILBasicBlock *finishBB = nullptr; /// A distributed actor may be 'remote' in which case there is no need to /// destroy "all" members, because they never had storage to begin with. @@ -393,7 +389,8 @@ void SILGenFunction::emitClassMemberDestruction(ManagedValue selfValue, // recursively the same type as `self`, so we can iteratively // deinitialize them, to prevent deep recursion and potential // stack overflows. - llvm::SmallSetVector recursiveLinks; + + llvm::SmallSetVector recursiveLinks; findRecursiveLinks(cd, recursiveLinks); /// Destroy all members. diff --git a/lib/SILGen/SILGenFunction.h b/lib/SILGen/SILGenFunction.h index 3d183afbb6b22..a18f563ba684d 100644 --- a/lib/SILGen/SILGenFunction.h +++ b/lib/SILGen/SILGenFunction.h @@ -683,7 +683,7 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction /// Generates code to destroy linearly recursive data structures, without /// building up the call stack. /// - /// Example: + /// E.x.: In the following we want to deinit next without recursing into next. /// /// class Node { /// let value: A From 51e5408eeb7a19fa031034d528a8cbfb6da3e469 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 8 Mar 2022 15:39:40 -0800 Subject: [PATCH 021/242] [ClangImporter] Allow @Sendable on more params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `__attribute__((swift_attr(“@Sendable”)))` is applied to a clang parameter Swift now attempts to make the type `Sendable` in a more comprehensive way than before: * If it is a function type, it adds `@Sendable` to it. * If it is a protocol composition type, it adds `& Sendable` to it. * If it is a class, protocol, or generic class, it inserts it into a protocol composition and adds `& Sendable`. * If it is a typealias, it *may* desugar it to a modified version of the underlying type. * In various other cases, it recurses into the children of the type. This allows Objective-C methods and functions to require that their arguments have a Sendable type without specifying a particular type they must belong to. Fixes rdar://87727549. --- .../swift/AST/DiagnosticsClangImporter.def | 8 + lib/ClangImporter/ImportType.cpp | 212 +++++++++++++++++- test/ClangImporter/objc_async.swift | 53 +++++ test/IDE/print_clang_objc_async.swift | 17 ++ .../usr/include/ObjCConcurrency.h | 22 ++ 5 files changed, 309 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/DiagnosticsClangImporter.def b/include/swift/AST/DiagnosticsClangImporter.def index 1ab31e0c1e001..128c4615a0596 100644 --- a/include/swift/AST/DiagnosticsClangImporter.def +++ b/include/swift/AST/DiagnosticsClangImporter.def @@ -89,6 +89,14 @@ WARNING(clang_error_code_must_be_sendable,none, "cannot make error code type '%0' non-sendable because Swift errors " "are always sendable", (StringRef)) +WARNING(clang_param_ignored_sendable_attr,none, + "cannot make parameter '%0' sendable type %1 cannot be made sendable " + "by adding '@Sendable' or '& Sendable'", + (StringRef, Type)) +NOTE(clang_param_should_be_implicitly_sendable,none, + "parameter should be implicitly 'Sendable' because it is a completion " + "handler", ()) + WARNING(implicit_bridging_header_imported_from_module,none, "implicit import of bridging header '%0' via module %1 " "is deprecated and will be removed in a later version of Swift", diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index c19a50cc1abb6..66f79cf208f27 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -30,6 +30,7 @@ #include "swift/AST/ParameterList.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" +#include "swift/AST/TypeVisitor.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Parse/Token.h" #include "swift/Strings.h" @@ -1733,6 +1734,197 @@ ImportedType ClangImporter::Implementation::importPropertyType( Bridgeability::Full, optionality); } +namespace { + +class GetSendableType : + private TypeVisitor> { + ASTContext &ctx; + +public: + GetSendableType(ASTContext &ctx) : ctx(ctx) {} + + /// The result of a conversion. Contains the converted type and a \c bool that + /// is \c true if the operation found something to change, or \c false + /// otherwise. + using Result = std::pair; + + /// Returns a modified version of \p type that has been made explicitly + /// \c Sendable by adding an \c \@Sendable attribute to a function type + /// or forming a protocol composition with \c & \c Sendable. + Result convert(Type type) { return visit(type); } + +private: + /// Decide how to represent the given type in a protocol composition. This + /// is specialized for \c ProtocolCompositionType to avoid nesting + /// compositions. + /// + /// \param members The types to include in the composition. + /// \return \c true if the composition should include \c AnyObject, \c false + /// otherwise. + bool getAsComposition(ProtocolCompositionType *ty, + SmallVectorImpl &members) { + llvm::append_range(members, ty->getMembers()); + return ty->hasExplicitAnyObject(); + } + + /// Decide how to represent the given type in a protocol composition. This + /// is specialized for \c ProtocolCompositionType to avoid nesting + /// compositions. + /// + /// \param members The types to include in the composition. + /// \return \c true if the composition should include \c AnyObject, \c false + /// otherwise. + bool getAsComposition(TypeBase *ty, SmallVectorImpl &members) { + members.push_back(ty); + return false; + } + + // MARK: Visitor Actions + + /// Visitor action: Replace this type with a protocol composition that + /// includes \c Sendable. + template Result compose(Ty *ty) { + SmallVector members; + bool explicitAnyObject = getAsComposition(ty, members); + + auto proto = ctx.getProtocol(KnownProtocolKind::Sendable); + members.push_back(proto->getDeclaredInterfaceType()); + + return { + ProtocolCompositionType::get(ctx, members, explicitAnyObject), true }; + } + + /// Visitor action: Recurse into the children of this type and try to add + /// \c Sendable to them. + Result recurse(Type ty) { + bool anyFound = false; + + Type newTy = ty.transformRec([&](TypeBase *childTy) -> Optional { + // We want to visit the first level of children. + if (childTy == ty.getPointer()) + return None; + + auto result = this->visit(childTy); + anyFound |= result.second; + return result.first; + }); + + return { newTy, anyFound }; + } + + /// Visitor action: Ignore this type; do not modify it and do not recurse into + /// it to find other types to modify. + Result pass(Type ty, bool found = false) { + return { ty, found }; + } + + // Macros to define visitors based on these actions. +#define VISIT(CLASS, ACT) Result visit##CLASS(CLASS *ty) { return ACT(ty); } +#define NEVER_VISIT(CLASS) Result visit##CLASS(CLASS *ty) { \ + llvm_unreachable("can't have " #CLASS " in imported clang type"); \ + return pass(ty); \ + } + + // MARK: Visitors + + friend TypeVisitor; + + Result visitErrorType(ErrorType *ty) { + // Pass, but suppress diagnostic about not finding anything `Sendable`. + return pass(ty, /*found=*/true); + } + + NEVER_VISIT(UnresolvedType) + NEVER_VISIT(PlaceholderType) + NEVER_VISIT(BuiltinType) + + VISIT(TupleType, recurse) + + NEVER_VISIT(ReferenceStorageType) + + VISIT(EnumType, pass) + VISIT(StructType, pass) + VISIT(ClassType, compose) + VISIT(ProtocolType, compose) + + Result visitBoundGenericType(BoundGenericType *ty) { + assert(!isa(ty) && "classes handled elsewhere"); + + // These types are produced during bridging and have conditional + // conformances to Sendable depending on their generic parameters, so we + // want to make their generic parameters `Sendable`. + if (ty->isOptional() || ty->isArray() || ty->isSet() || + ty->isDictionary()) + return recurse(ty); + + // Other non-class generic types (e.g. pointers) cannot be made Sendable. + return pass(ty); + } + + VISIT(BoundGenericClassType, compose) + NEVER_VISIT(UnboundGenericType) + + VISIT(AnyMetatypeType, recurse) + + VISIT(ModuleType, pass) + VISIT(DynamicSelfType, pass) + + NEVER_VISIT(SubstitutableType) + NEVER_VISIT(DependentMemberType) + + Result visitAnyFunctionType(AnyFunctionType *ty) { + auto newFn = applyToFunctionType(ty, [](ASTExtInfo extInfo) { + return extInfo.withConcurrent(); + }); + return { newFn, true }; + } + + NEVER_VISIT(SILFunctionType) + NEVER_VISIT(SILBlockStorageType) + NEVER_VISIT(SILBoxType) + NEVER_VISIT(SILTokenType) + + VISIT(ProtocolCompositionType, compose) + + // ProtocolCompositionType doesn't handle ParameterizedProtocolType + // correctly, but we currently never import anything with it, so forbid it + // until we find we need it. + NEVER_VISIT(ParameterizedProtocolType) + + VISIT(ExistentialType, recurse) + NEVER_VISIT(LValueType) + VISIT(InOutType, recurse) + + NEVER_VISIT(PackType) + NEVER_VISIT(PackExpansionType) + NEVER_VISIT(TypeVariableType) + + VISIT(SugarType, recurse) + + Result visitTypeAliasType(TypeAliasType *ty) { + // Try converting the underlying type. + Type underlying = ty->getSinglyDesugaredType(); + auto result = visit(underlying); + + // If nothing that could be made Sendable was found in the underlying type, + // keep the sugar. + if (!result.second) + return pass(ty); + + // If something Sendable-capable *was* found but the operation was a no-op, + // keep the sugar but indicate that we did find something to avoid a + // diagnostic. + if (result.first->getCanonicalType() == underlying->getCanonicalType()) + return pass(ty, /*found=*/true); + + // We found something and it did change the type. Desugar to the converted + // underlying type. + return result; + } +}; + +} // anonymous namespace + Type ClangImporter::Implementation::applyParamAttributes( const clang::ParmVarDecl *param, Type type, bool sendableByDefault) { bool sendableRequested = sendableByDefault; @@ -1780,9 +1972,23 @@ Type ClangImporter::Implementation::applyParamAttributes( } if (!sendableDisqualified && sendableRequested) { - type = applyToFunctionType(type, [](ASTExtInfo extInfo) { - return extInfo.withConcurrent(); - }); + bool changed; + std::tie(type, changed) = GetSendableType(SwiftContext).convert(type); + + // Diagnose if we couldn't find a place to add `Sendable` to the type. + if (!changed) { + auto parentDecl = cast(param->getDeclContext()); + + addImportDiagnostic(parentDecl, + Diagnostic(diag::clang_param_ignored_sendable_attr, + param->getName(), type), + param->getLocation()); + + if (sendableByDefault) + addImportDiagnostic(parentDecl, + Diagnostic(diag::clang_param_should_be_implicitly_sendable), + param->getLocation()); + } } return type; diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index 1bf91117ccb77..e0ed183f1e88a 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -295,4 +295,57 @@ func check() async { _ = await BazFrame(size: 0) } +func testSender( + sender: NXSender, + sendableObject: SendableClass, + nonSendableObject: NonSendableClass, + sendableSubclassOfNonSendableObject: NonSendableClass & Sendable, + sendableProtos: LabellyProtocol & ObjCClub & Sendable, + nonSendableProtos: LabellyProtocol & ObjCClub, + sendableGeneric: GenericObject & Sendable, + nonSendableGeneric: GenericObject, + ptr: UnsafeMutableRawPointer, + stringArray: [String] +) { + sender.sendAny(sendableObject) + sender.sendAny(nonSendableObject) + // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} + + sender.sendOptionalAny(sendableObject) + sender.sendOptionalAny(nonSendableObject) + // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} + + sender.sendSendable(sendableObject) + + sender.sendSendableSubclasses(nonSendableObject) + // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} + sender.sendSendableSubclasses(sendableSubclassOfNonSendableObject) + // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} + // FIXME(rdar://89992569): Should allow for the possibility that NonSendableClass will have a Sendable subclass + + sender.sendProto(sendableProtos) + sender.sendProto(nonSendableProtos) + // expected-error@-1 {{argument type 'any LabellyProtocol & ObjCClub' does not conform to expected type 'Sendable'}} + // FIXME(rdar://89992095): Should be a warning because we're in -warn-concurrency + + sender.sendProtos(sendableProtos) + sender.sendProtos(nonSendableProtos) + // expected-error@-1 {{argument type 'any LabellyProtocol & ObjCClub' does not conform to expected type 'Sendable'}} + // FIXME(rdar://89992095): Should be a warning because we're in -warn-concurrency + + sender.sendAnyArray([sendableObject]) + sender.sendAnyArray([nonSendableObject]) + // expected-warning@-1 {{conformance of 'NonSendableClass' to 'Sendable' is unavailable}} + + sender.sendGeneric(sendableGeneric) + // expected-warning@-1 {{type 'GenericObject' does not conform to the 'Sendable' protocol}} + // FIXME(rdar://89992569): Should allow for the possibility that GenericObject will have a Sendable subclass + sender.sendGeneric(nonSendableGeneric) + // expected-error@-1 {{argument type 'GenericObject' does not conform to expected type 'Sendable'}} + // FIXME(rdar://89992095): Should be a warning because we're in -warn-concurrency + + sender.sendPtr(ptr) + sender.sendStringArray(stringArray) +} + } // SwiftStdlib 5.5 diff --git a/test/IDE/print_clang_objc_async.swift b/test/IDE/print_clang_objc_async.swift index b9229baef5932..ee4d8981e88ca 100644 --- a/test/IDE/print_clang_objc_async.swift +++ b/test/IDE/print_clang_objc_async.swift @@ -124,3 +124,20 @@ import _Concurrency // CHECK: func doSomethingConcurrently(_ block: @Sendable () -> Void) // CHECK: @MainActor @objc protocol TripleMainActor { + +// CHECK-LABEL: class NXSender : +// CHECK-NEXT: func sendAny(_ obj: Sendable) +// CHECK-NEXT: func sendOptionalAny(_ obj: Sendable?) +// CHECK-NEXT: func sendSendable(_ sendable: SendableClass & Sendable) +// CHECK-NEXT: func sendSendableSubclasses(_ sendableSubclass: NonSendableClass & Sendable) +// CHECK-NEXT: func sendProto(_ obj: LabellyProtocol & Sendable) +// CHECK-NEXT: func sendProtos(_ obj: LabellyProtocol & ObjCClub & Sendable) +// CHECK-NEXT: func sendAnyArray(_ array: [Sendable]) +// CHECK-NEXT: func sendGeneric(_ generic: GenericObject & Sendable) +// CHECK-NEXT: func sendPtr(_ val: UnsafeMutableRawPointer) +// CHECK-NEXT: func sendStringArray(_ obj: [String]) +// CHECK-NEXT: func sendAnyTypedef(_ obj: Sendable) +// CHECK-NEXT: func sendAnyTypedefs(_ objs: [Sendable]) +// CHECK-NEXT: func sendBlockTypedef(_ block: @escaping @Sendable (Any) -> Void) +// CHECK-NEXT: func sendBlockTypedefs(_ blocks: [@Sendable @convention(block) (Any) -> Void]) +// CHECK-NEXT: func sendUnbound(_ array: [Sendable]) diff --git a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h index d33c036c45370..a4842c386f504 100644 --- a/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h +++ b/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h @@ -267,4 +267,26 @@ typedef NSString *NonSendableStringStruct NS_EXTENSIBLE_STRING_ENUM NONSENDABLE; ASSUME_NONSENDABLE_END +typedef id ObjectTypedef; +typedef void(^BlockTypedef)(id); + +@interface NXSender : NSObject + +- (void)sendAny:(SENDABLE id)obj; +- (void)sendOptionalAny:(nullable SENDABLE id)obj; +- (void)sendSendable:(SENDABLE SendableClass *)sendable; +- (void)sendSendableSubclasses:(SENDABLE NonSendableClass *)sendableSubclass; +- (void)sendProto:(SENDABLE id )obj; +- (void)sendProtos:(SENDABLE id )obj; +- (void)sendAnyArray:(SENDABLE NSArray *)array; +- (void)sendGeneric:(SENDABLE GenericObject *)generic; +- (void)sendPtr:(SENDABLE void *)val; // bad +- (void)sendStringArray:(SENDABLE NSArray *)obj; // bad +- (void)sendAnyTypedef:(SENDABLE ObjectTypedef)obj; +- (void)sendAnyTypedefs:(SENDABLE NSArray *)objs; +- (void)sendBlockTypedef:(SENDABLE BlockTypedef)block; +- (void)sendBlockTypedefs:(SENDABLE NSArray *)blocks; +- (void)sendUnbound:(SENDABLE NSArray *)array; +@end + #pragma clang assume_nonnull end From 504e73d5ad39302e26f446812298ab3f3f72d931 Mon Sep 17 00:00:00 2001 From: Rintaro Ishizaki Date: Fri, 11 Mar 2022 10:07:14 -0800 Subject: [PATCH 022/242] [CodeCompletion] Remove a hack adding type matching nominal types Previously we didn't give any type relations to cached imported symbols. So there was a hack to add the type matching nominal type with type relation manually, which caused duplicated candiates, one from the cache, one added manually. Now that we have type relations for cached symbols so we don't need this hack anymore. rdar://90136020 --- lib/IDE/CompletionLookup.cpp | 16 ---------------- test/IDE/complete_value_expr.swift | 8 ++++++++ 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index e116e483a858c..93d6208bfd574 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -2634,22 +2634,6 @@ void CompletionLookup::getValueCompletionsInDeclContext(SourceLoc Loc, RequestedResultsTy::toplevelResults().withModuleQualifier( ModuleQualifier)); - // Manually add any expected nominal types from imported modules so that - // they get their expected type relation. Don't include protocols, since - // they can't be initialized from the type name. - // FIXME: this does not include types that conform to an expected protocol. - // FIXME: this creates duplicate results. - for (auto T : expectedTypeContext.getPossibleTypes()) { - if (auto NT = T->getAs()) { - if (auto NTD = NT->getDecl()) { - if (!isa(NTD) && NTD->getModuleContext() != CurrModule) { - addNominalTypeRef(NT->getDecl(), - DeclVisibilityKind::VisibleAtTopLevel, {}); - } - } - } - } - if (CompletionContext) { // FIXME: this is an awful simplification that says all and only enums can // use implicit member syntax (leading dot). Computing the accurate answer diff --git a/test/IDE/complete_value_expr.swift b/test/IDE/complete_value_expr.swift index cb02877933e1f..da650f53f5dff 100644 --- a/test/IDE/complete_value_expr.swift +++ b/test/IDE/complete_value_expr.swift @@ -2041,3 +2041,11 @@ func testProtocolMetatype(protoProto: MetaProto.Protocol, protoType: MetaProto.T // PROTOCOLMETA_3: End completions } +func testRdar90136020() { + let a: Int64 = #^RDAR90136020^# +// RDAR90136020: Begin completions +// RDAR90136020-NOT: name=Int64{{$}} +// RDAR90136020: Decl[Struct]/OtherModule[Swift]/IsSystem/TypeRelation[Identical]: Int64[#Int64#]; name=Int64 +// RDAR90136020-NOT: name=Int64{{$}} +// RDAR90136020: End completions +} From 858a847d2786823475a0ca88e52abf5027fcdca8 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Sun, 13 Mar 2022 11:08:32 -0700 Subject: [PATCH 023/242] [cxx-interop] use SWIFT_NOEXCEPT for Swift function prototypes This will allow these functions to be used from C. Also add a static_assert to verify that noexcept is actually used. --- lib/PrintAsClang/DeclAndTypePrinter.cpp | 2 +- lib/PrintAsClang/PrintAsClang.cpp | 6 ++++++ test/Interop/SwiftToCxx/functions/cdecl.swift | 2 +- .../SwiftToCxx/functions/function-availability.swift | 10 +++++----- .../SwiftToCxx/functions/swift-functions-execution.cpp | 4 ++++ .../Interop/SwiftToCxx/functions/swift-functions.swift | 10 +++++----- 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index 355ba6421561a..20746affe4d2f 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -848,7 +848,7 @@ class DeclAndTypePrinter::Implementation // Swift functions can't throw exceptions, we can only // throw them from C++ when emitting C++ inline thunks for the Swift // functions. - os << " noexcept"; + os << " SWIFT_NOEXCEPT"; if (!funcABI.useCCallingConvention()) os << " SWIFT_CALL"; printAvailability(FD); diff --git a/lib/PrintAsClang/PrintAsClang.cpp b/lib/PrintAsClang/PrintAsClang.cpp index 0b3e302222bae..ec23d0b12416e 100644 --- a/lib/PrintAsClang/PrintAsClang.cpp +++ b/lib/PrintAsClang/PrintAsClang.cpp @@ -272,6 +272,12 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, out << "#endif\n"; }; emitMacro("SWIFT_CALL", "__attribute__((swiftcall))"); + // SWIFT_NOEXCEPT applies 'noexcept' in C++ mode only. + out << "#if defined(__cplusplus)\n"; + emitMacro("SWIFT_NOEXCEPT", "noexcept"); + out << "#else\n"; + emitMacro("SWIFT_NOEXCEPT", ""); + out << "#endif\n"; static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, "need to add SIMD typedefs here if max elements is increased"); } diff --git a/test/Interop/SwiftToCxx/functions/cdecl.swift b/test/Interop/SwiftToCxx/functions/cdecl.swift index 67b8ce223e43b..7b8a21d8f7b97 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl.swift +++ b/test/Interop/SwiftToCxx/functions/cdecl.swift @@ -7,7 +7,7 @@ // CHECK-LABEL: namespace CdeclFunctions { // CHECK: namespace _impl { -// CHECK: extern "C" int cfuncPassTwo(int x, int y) noexcept; +// CHECK: extern "C" int cfuncPassTwo(int x, int y) SWIFT_NOEXCEPT; // CHECK: } @_cdecl("cfuncPassTwo") diff --git a/test/Interop/SwiftToCxx/functions/function-availability.swift b/test/Interop/SwiftToCxx/functions/function-availability.swift index 6242e5130d905..d0371fb153aeb 100644 --- a/test/Interop/SwiftToCxx/functions/function-availability.swift +++ b/test/Interop/SwiftToCxx/functions/function-availability.swift @@ -8,11 +8,11 @@ // CHECK-LABEL: namespace _impl { -// CHECK: extern "C" void $s9Functions16alwaysDeprecatedyyF(void) noexcept SWIFT_CALL SWIFT_DEPRECATED; // alwaysDeprecated() -// CHECK: extern "C" void $s9Functions19alwaysDeprecatedTwoyyF(void) noexcept SWIFT_CALL SWIFT_DEPRECATED_MSG("it should not be used"); // alwaysDeprecatedTwo() -// CHECK: extern "C" void $s9Functions17alwaysUnavailableyyF(void) noexcept SWIFT_CALL SWIFT_UNAVAILABLE; // alwaysUnavailable() -// CHECK: extern "C" void $s9Functions24alwaysUnavailableMessageyyF(void) noexcept SWIFT_CALL SWIFT_UNAVAILABLE_MSG("stuff happened"); // alwaysUnavailableMessage() -// CHECK: extern "C" void $s9Functions22singlePlatAvailabilityyyF(void) noexcept SWIFT_CALL SWIFT_AVAILABILITY(macos,introduced=11); // singlePlatAvailability() +// CHECK: extern "C" void $s9Functions16alwaysDeprecatedyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_DEPRECATED; // alwaysDeprecated() +// CHECK: extern "C" void $s9Functions19alwaysDeprecatedTwoyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_DEPRECATED_MSG("it should not be used"); // alwaysDeprecatedTwo() +// CHECK: extern "C" void $s9Functions17alwaysUnavailableyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_UNAVAILABLE; // alwaysUnavailable() +// CHECK: extern "C" void $s9Functions24alwaysUnavailableMessageyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_UNAVAILABLE_MSG("stuff happened"); // alwaysUnavailableMessage() +// CHECK: extern "C" void $s9Functions22singlePlatAvailabilityyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_AVAILABILITY(macos,introduced=11); // singlePlatAvailability() // CHECK: } diff --git a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp index 70c19d17ffb07..08a7d0c5475a9 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp +++ b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp @@ -14,6 +14,10 @@ #include "functions.h" int main() { + static_assert(noexcept(Functions::passVoidReturnVoid()), "noexcept function"); + static_assert(noexcept(Functions::_impl::$s9Functions014passVoidReturnC0yyF()), + "noexcept function"); + Functions::passVoidReturnVoid(); Functions::passIntReturnVoid(-1); assert(Functions::passTwoIntReturnIntNoArgLabel(1, 2) == 42); diff --git a/test/Interop/SwiftToCxx/functions/swift-functions.swift b/test/Interop/SwiftToCxx/functions/swift-functions.swift index e7516448cbb9e..a4634e7a380a1 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions.swift +++ b/test/Interop/SwiftToCxx/functions/swift-functions.swift @@ -8,11 +8,11 @@ // CHECK-LABEL: namespace _impl { -// CHECK: extern "C" void $s9Functions17passIntReturnVoid1xys5Int32V_tF(int x) noexcept SWIFT_CALL; // passIntReturnVoid(x:) -// CHECK: extern "C" int $s9Functions016passTwoIntReturnD01x1ys5Int32VAF_AFtF(int x, int y) noexcept SWIFT_CALL; // passTwoIntReturnInt(x:y:) -// CHECK: extern "C" int $s9Functions016passTwoIntReturnD10NoArgLabelys5Int32VAD_ADtF(int, int) noexcept SWIFT_CALL; // passTwoIntReturnIntNoArgLabel(_:_:) -// CHECK: extern "C" int $s9Functions016passTwoIntReturnD19NoArgLabelParamNameys5Int32VAD_ADtF(int x2, int y2) noexcept SWIFT_CALL; // passTwoIntReturnIntNoArgLabelParamName(_:_:) -// CHECK: extern "C" void $s9Functions014passVoidReturnC0yyF(void) noexcept SWIFT_CALL; // passVoidReturnVoid() +// CHECK: extern "C" void $s9Functions17passIntReturnVoid1xys5Int32V_tF(int x) SWIFT_NOEXCEPT SWIFT_CALL; // passIntReturnVoid(x:) +// CHECK: extern "C" int $s9Functions016passTwoIntReturnD01x1ys5Int32VAF_AFtF(int x, int y) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnInt(x:y:) +// CHECK: extern "C" int $s9Functions016passTwoIntReturnD10NoArgLabelys5Int32VAD_ADtF(int, int) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnIntNoArgLabel(_:_:) +// CHECK: extern "C" int $s9Functions016passTwoIntReturnD19NoArgLabelParamNameys5Int32VAD_ADtF(int x2, int y2) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnIntNoArgLabelParamName(_:_:) +// CHECK: extern "C" void $s9Functions014passVoidReturnC0yyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // passVoidReturnVoid() // CHECK: } From 6b8108bf59d8823d25c92e8843db7cbffc46c759 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Sun, 13 Mar 2022 12:11:35 -0700 Subject: [PATCH 024/242] [cxx-interop] use SWIFT_EXTERN for Swift function prototypes This will allow these functions to be used from C. --- lib/PrintAsClang/DeclAndTypePrinter.cpp | 2 +- test/Interop/SwiftToCxx/functions/cdecl.swift | 2 +- .../SwiftToCxx/functions/function-availability.swift | 10 +++++----- .../Interop/SwiftToCxx/functions/swift-functions.swift | 10 +++++----- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index 20746affe4d2f..5c8c82ae0b0bf 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -843,7 +843,7 @@ class DeclAndTypePrinter::Implementation Mangle::ASTMangler mangler; FuncionSwiftABIInformation funcABI(FD, mangler); - os << "extern \"C\" "; + os << "SWIFT_EXTERN "; printFunctionDeclAsCFunctionDecl(FD, funcABI.getSymbolName(), resultTy); // Swift functions can't throw exceptions, we can only // throw them from C++ when emitting C++ inline thunks for the Swift diff --git a/test/Interop/SwiftToCxx/functions/cdecl.swift b/test/Interop/SwiftToCxx/functions/cdecl.swift index 7b8a21d8f7b97..8b1c73585b9d1 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl.swift +++ b/test/Interop/SwiftToCxx/functions/cdecl.swift @@ -7,7 +7,7 @@ // CHECK-LABEL: namespace CdeclFunctions { // CHECK: namespace _impl { -// CHECK: extern "C" int cfuncPassTwo(int x, int y) SWIFT_NOEXCEPT; +// CHECK: SWIFT_EXTERN int cfuncPassTwo(int x, int y) SWIFT_NOEXCEPT; // CHECK: } @_cdecl("cfuncPassTwo") diff --git a/test/Interop/SwiftToCxx/functions/function-availability.swift b/test/Interop/SwiftToCxx/functions/function-availability.swift index d0371fb153aeb..54f9dfb1d958e 100644 --- a/test/Interop/SwiftToCxx/functions/function-availability.swift +++ b/test/Interop/SwiftToCxx/functions/function-availability.swift @@ -8,11 +8,11 @@ // CHECK-LABEL: namespace _impl { -// CHECK: extern "C" void $s9Functions16alwaysDeprecatedyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_DEPRECATED; // alwaysDeprecated() -// CHECK: extern "C" void $s9Functions19alwaysDeprecatedTwoyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_DEPRECATED_MSG("it should not be used"); // alwaysDeprecatedTwo() -// CHECK: extern "C" void $s9Functions17alwaysUnavailableyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_UNAVAILABLE; // alwaysUnavailable() -// CHECK: extern "C" void $s9Functions24alwaysUnavailableMessageyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_UNAVAILABLE_MSG("stuff happened"); // alwaysUnavailableMessage() -// CHECK: extern "C" void $s9Functions22singlePlatAvailabilityyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_AVAILABILITY(macos,introduced=11); // singlePlatAvailability() +// CHECK: SWIFT_EXTERN void $s9Functions16alwaysDeprecatedyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_DEPRECATED; // alwaysDeprecated() +// CHECK: SWIFT_EXTERN void $s9Functions19alwaysDeprecatedTwoyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_DEPRECATED_MSG("it should not be used"); // alwaysDeprecatedTwo() +// CHECK: SWIFT_EXTERN void $s9Functions17alwaysUnavailableyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_UNAVAILABLE; // alwaysUnavailable() +// CHECK: SWIFT_EXTERN void $s9Functions24alwaysUnavailableMessageyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_UNAVAILABLE_MSG("stuff happened"); // alwaysUnavailableMessage() +// CHECK: SWIFT_EXTERN void $s9Functions22singlePlatAvailabilityyyF(void) SWIFT_NOEXCEPT SWIFT_CALL SWIFT_AVAILABILITY(macos,introduced=11); // singlePlatAvailability() // CHECK: } diff --git a/test/Interop/SwiftToCxx/functions/swift-functions.swift b/test/Interop/SwiftToCxx/functions/swift-functions.swift index a4634e7a380a1..e37e24bff13ce 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions.swift +++ b/test/Interop/SwiftToCxx/functions/swift-functions.swift @@ -8,11 +8,11 @@ // CHECK-LABEL: namespace _impl { -// CHECK: extern "C" void $s9Functions17passIntReturnVoid1xys5Int32V_tF(int x) SWIFT_NOEXCEPT SWIFT_CALL; // passIntReturnVoid(x:) -// CHECK: extern "C" int $s9Functions016passTwoIntReturnD01x1ys5Int32VAF_AFtF(int x, int y) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnInt(x:y:) -// CHECK: extern "C" int $s9Functions016passTwoIntReturnD10NoArgLabelys5Int32VAD_ADtF(int, int) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnIntNoArgLabel(_:_:) -// CHECK: extern "C" int $s9Functions016passTwoIntReturnD19NoArgLabelParamNameys5Int32VAD_ADtF(int x2, int y2) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnIntNoArgLabelParamName(_:_:) -// CHECK: extern "C" void $s9Functions014passVoidReturnC0yyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // passVoidReturnVoid() +// CHECK: SWIFT_EXTERN void $s9Functions17passIntReturnVoid1xys5Int32V_tF(int x) SWIFT_NOEXCEPT SWIFT_CALL; // passIntReturnVoid(x:) +// CHECK: SWIFT_EXTERN int $s9Functions016passTwoIntReturnD01x1ys5Int32VAF_AFtF(int x, int y) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnInt(x:y:) +// CHECK: SWIFT_EXTERN int $s9Functions016passTwoIntReturnD10NoArgLabelys5Int32VAD_ADtF(int, int) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnIntNoArgLabel(_:_:) +// CHECK: SWIFT_EXTERN int $s9Functions016passTwoIntReturnD19NoArgLabelParamNameys5Int32VAD_ADtF(int x2, int y2) SWIFT_NOEXCEPT SWIFT_CALL; // passTwoIntReturnIntNoArgLabelParamName(_:_:) +// CHECK: SWIFT_EXTERN void $s9Functions014passVoidReturnC0yyF(void) SWIFT_NOEXCEPT SWIFT_CALL; // passVoidReturnVoid() // CHECK: } From b3fefb6032d7ed02cc63aa184acb2d75fc56cee1 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Mon, 14 Mar 2022 11:13:35 +0000 Subject: [PATCH 025/242] Revert "[test] Disable debug_value_addr.swift on arm64" This reverts commit 0a7a976d31edb7bd052b8dfaa25d8ed5bb59381e. --- test/DebugInfo/debug_value_addr.swift | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/DebugInfo/debug_value_addr.swift b/test/DebugInfo/debug_value_addr.swift index 3a8e94364f2c9..06f5b187a1daf 100644 --- a/test/DebugInfo/debug_value_addr.swift +++ b/test/DebugInfo/debug_value_addr.swift @@ -1,9 +1,6 @@ // RUN: %target-swift-frontend -primary-file %s -emit-ir -g -o - | %FileCheck %s // RUN: %target-swift-frontend %s -emit-sil -g -o - | %FileCheck -check-prefix=CHECK-SIL %s -// Temporarily disable on arm64 (rdar://89237318) -// UNSUPPORTED: CPU=arm64 - // Verify that -Onone shadow copies are emitted for debug_value_addr // instructions. From 82cb46c8b0956e3acd32f976b1b9bfb8b91ed729 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 4 Mar 2022 10:28:15 +0100 Subject: [PATCH 026/242] [CodeCompletion] Complete code completion tokens after if-statement as top-level MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of setting the code completion position when parsing the if-statement, which doesn’t create a `CodeCompletionExpr`, parse it as a new top-level expression. As far as test-cases are concerned, this removes the “RareKeyword” flair from top-level completions in the modified test case. This makes sense IMO. --- include/swift/Parse/CodeCompletionCallbacks.h | 2 +- lib/IDE/CodeCompletion.cpp | 10 +++------- lib/Parse/ParseStmt.cpp | 7 +------ test/IDE/complete_keywords.swift | 5 ++++- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/include/swift/Parse/CodeCompletionCallbacks.h b/include/swift/Parse/CodeCompletionCallbacks.h index 6bb9f57342cd1..3de2afc37cfdd 100644 --- a/include/swift/Parse/CodeCompletionCallbacks.h +++ b/include/swift/Parse/CodeCompletionCallbacks.h @@ -225,7 +225,7 @@ class CodeCompletionCallbacks { virtual void completePlatformCondition() {}; - virtual void completeAfterIfStmt(bool hasElse) {}; + virtual void completeAfterIfStmtElse() {}; virtual void completeGenericRequirement() {}; diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 7fbc3c76a5ee0..0b4abeb58b7b2 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -267,7 +267,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { void completeAfterPoundDirective() override; void completePlatformCondition() override; void completeGenericRequirement() override; - void completeAfterIfStmt(bool hasElse) override; + void completeAfterIfStmtElse() override; void completeStmtLabel(StmtKind ParentKind) override; void completeForEachPatternBeginning(bool hasTry, bool hasAwait) override; void completeTypeAttrBeginning() override; @@ -578,13 +578,9 @@ void CodeCompletionCallbacksImpl::completePlatformCondition() { Kind = CompletionKind::PlatformConditon; } -void CodeCompletionCallbacksImpl::completeAfterIfStmt(bool hasElse) { +void CodeCompletionCallbacksImpl::completeAfterIfStmtElse() { CurDeclContext = P.CurDeclContext; - if (hasElse) { - Kind = CompletionKind::AfterIfStmtElse; - } else { - Kind = CompletionKind::StmtOrExpr; - } + Kind = CompletionKind::AfterIfStmtElse; } void CodeCompletionCallbacksImpl::completeGenericRequirement() { diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index e8be43be76697..469fa1b019a32 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1807,18 +1807,13 @@ ParserResult Parser::parseStmtIf(LabeledStmtInfo LabelInfo, ElseBody = parseStmtIf(LabeledStmtInfo(), implicitlyInsertIf); } else if (Tok.is(tok::code_complete)) { if (CodeCompletion) - CodeCompletion->completeAfterIfStmt(/*hasElse*/true); + CodeCompletion->completeAfterIfStmtElse(); Status.setHasCodeCompletionAndIsError(); consumeToken(tok::code_complete); } else { ElseBody = parseBraceItemList(diag::expected_lbrace_or_if_after_else); } Status |= ElseBody; - } else if (Tok.is(tok::code_complete)) { - if (CodeCompletion) - CodeCompletion->completeAfterIfStmt(/*hasElse*/false); - Status.setHasCodeCompletionAndIsError(); - consumeToken(tok::code_complete); } return makeParserResult( diff --git a/test/IDE/complete_keywords.swift b/test/IDE/complete_keywords.swift index a7a8748e3f544..f49be21b57ec3 100644 --- a/test/IDE/complete_keywords.swift +++ b/test/IDE/complete_keywords.swift @@ -308,7 +308,10 @@ for _ in 1...10 { #^TOP_LEVEL_2?check=KW_DECL_STMT;check=KW_NO_RETURN^# } -if true {} #^TOP_LEVEL_AFTER_IF_1?check=KW_DECL_STMT;check=KW_NO_RETURN^# +if true {} #^TOP_LEVEL_AFTER_IF_1?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN^# +if true {} +#^TOP_LEVEL_AFTER_IF_2?check=KW_DECL_STMT_TOPLEVEL;check=KW_NO_RETURN^# + if true {} else #^TOP_LEVEL_AFTER_IF_ELSE_1?check=AFTER_IF_ELSE^# {} From 3576318fc742875f48c107e14e5b7b6064df6b56 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 10 Mar 2022 16:44:35 -0500 Subject: [PATCH 027/242] RequirementMachine: Refactor construction of requirements from rules The final step in minimization is building Requirements and ProtocolTypeAliases from the minimal Rules in the RewriteSystem. Move this to a new file and refactor it a bit to handle Requirements and ProtocolTypeAliases more consistently. --- lib/AST/CMakeLists.txt | 1 + .../RequirementMachine/RequirementBuilder.cpp | 360 ++++++++++++++++++ .../RequirementMachine/RequirementMachine.h | 13 +- .../RequirementMachineRequests.cpp | 288 +------------- 4 files changed, 388 insertions(+), 274 deletions(-) create mode 100644 lib/AST/RequirementMachine/RequirementBuilder.cpp diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index ce74b7fe8baf2..d23b1952f7d1b 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -87,6 +87,7 @@ add_swift_host_library(swiftAST STATIC RequirementMachine/PropertyMap.cpp RequirementMachine/PropertyRelations.cpp RequirementMachine/PropertyUnification.cpp + RequirementMachine/RequirementBuilder.cpp RequirementMachine/RequirementLowering.cpp RequirementMachine/RequirementMachine.cpp RequirementMachine/RequirementMachineRequests.cpp diff --git a/lib/AST/RequirementMachine/RequirementBuilder.cpp b/lib/AST/RequirementMachine/RequirementBuilder.cpp new file mode 100644 index 0000000000000..eb08ab3aaeb33 --- /dev/null +++ b/lib/AST/RequirementMachine/RequirementBuilder.cpp @@ -0,0 +1,360 @@ +//===--- RequirementBuilder.cpp - Building requirements from rules --------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements the final step in generic signature minimization, +// building requirements from a set of minimal, canonical rewrite rules. +// +//===----------------------------------------------------------------------===// + +#include "RequirementMachine.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Requirement.h" +#include "swift/AST/RequirementSignature.h" +#include "swift/AST/Types.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include + +using namespace swift; +using namespace rewriting; + +namespace { + +/// Represents a set of types related by same-type requirements, and an +/// optional concrete type requirement. +struct ConnectedComponent { + llvm::SmallVector Members; + llvm::SmallVector Aliases; + Type ConcreteType; + + void buildRequirements(Type subjectType, + std::vector &reqs, + std::vector &aliases); +}; + +/// Case 1: A set of rewrite rules of the form: +/// +/// B => A +/// C => A +/// D => A +/// +/// Become a series of same-type requirements +/// +/// A == B, B == C, C == D +/// +/// Case 2: A set of rewrite rules of the form: +/// +/// A.[concrete: X] => A +/// B => A +/// C => A +/// D => A +/// +/// Become a series of same-type requirements +/// +/// A == X, B == X, C == X, D == X +void ConnectedComponent::buildRequirements(Type subjectType, + std::vector &reqs, + std::vector &aliases) { + std::sort(Members.begin(), Members.end(), + [](Type first, Type second) -> bool { + return compareDependentTypes(first, second) < 0; + }); + + if (!ConcreteType) { + for (auto name : Aliases) { + aliases.emplace_back(name, subjectType); + } + + for (auto constraintType : Members) { + reqs.emplace_back(RequirementKind::SameType, + subjectType, constraintType); + subjectType = constraintType; + } + + // For compatibility with the old GenericSignatureBuilder, drop requirements + // containing ErrorTypes. + } else if (!ConcreteType->hasError()) { + // If there are multiple protocol typealiases in the connected component, + // lower them all to a series of identical concrete-type aliases. + for (auto name : Aliases) { + aliases.emplace_back(name, ConcreteType); + } + + // If the most canonical representative in the connected component is an + // unresolved DependentMemberType, it must be of the form 'Self.A' + // where 'A' is an alias. Emit the concrete-type alias itself. + if (auto *memberTy = subjectType->getAs()) { + if (memberTy->getAssocType() == nullptr) { + auto *paramTy = memberTy->getBase()->castTo(); + assert(paramTy->getDepth() == 0 && paramTy->getIndex() == 0); + (void) paramTy; + + aliases.emplace_back(memberTy->getName(), ConcreteType); + + assert(Members.empty()); + return; + } + } + + // Otherwise, the most canonical representative must be a resolved + // associated type. Emit a requirement. + reqs.emplace_back(RequirementKind::SameType, + subjectType, ConcreteType); + + // Finally, emit a concrete type requirement for all resolved type members + // of the connected component. + for (auto constraintType : Members) { + reqs.emplace_back(RequirementKind::SameType, + constraintType, ConcreteType); + } + } +} + +/// Once we're done with minimization, we turn the minimal rules into requirements. +/// This is in a sense the inverse of RuleBuilder in RequirementLowering.cpp. +class RequirementBuilder { + // Input parameters. + const RewriteSystem &System; + const PropertyMap ⤅ + TypeArrayView GenericParams; + bool Debug; + + // Temporary state populated by addRequirementRules() and + // addTypeAliasRules(). + llvm::SmallDenseMap Components; + +public: + // Results. + std::vector Reqs; + std::vector Aliases; + + RequirementBuilder(const RewriteSystem &system, const PropertyMap &map, + TypeArrayView genericParams) + : System(system), Map(map), GenericParams(genericParams), + Debug(System.getDebugOptions().contains(DebugFlags::Minimization)) {} + + void addRequirementRules(ArrayRef rules); + void addTypeAliasRules(ArrayRef rules); + + void processConnectedComponents(); + + void sortRequirements(); + void sortTypeAliases(); +}; + +} // end namespace + +void RequirementBuilder::addRequirementRules(ArrayRef rules) { + // Convert a rewrite rule into a requirement. + auto createRequirementFromRule = [&](const Rule &rule) { + if (auto prop = rule.isPropertyRule()) { + auto subjectType = Map.getTypeForTerm(rule.getRHS(), GenericParams); + + switch (prop->getKind()) { + case Symbol::Kind::Protocol: + Reqs.emplace_back(RequirementKind::Conformance, + subjectType, + prop->getProtocol()->getDeclaredInterfaceType()); + return; + + case Symbol::Kind::Layout: + Reqs.emplace_back(RequirementKind::Layout, + subjectType, + prop->getLayoutConstraint()); + return; + + case Symbol::Kind::Superclass: { + // Requirements containing unresolved name symbols originate from + // invalid code and should not appear in the generic signature. + for (auto term : prop->getSubstitutions()) { + if (term.containsUnresolvedSymbols()) + return; + } + + // Requirements containing error types originate from invalid code + // and should not appear in the generic signature. + if (prop->getConcreteType()->hasError()) + return; + + auto superclassType = Map.getTypeFromSubstitutionSchema( + prop->getConcreteType(), + prop->getSubstitutions(), + GenericParams, MutableTerm()); + + Reqs.emplace_back(RequirementKind::Superclass, + subjectType, superclassType); + return; + } + + case Symbol::Kind::ConcreteType: { + // Requirements containing unresolved name symbols originate from + // invalid code and should not appear in the generic signature. + for (auto term : prop->getSubstitutions()) { + if (term.containsUnresolvedSymbols()) + return; + } + + // Requirements containing error types originate from invalid code + // and should not appear in the generic signature. + if (prop->getConcreteType()->hasError()) + return; + + auto concreteType = Map.getTypeFromSubstitutionSchema( + prop->getConcreteType(), + prop->getSubstitutions(), + GenericParams, MutableTerm()); + + auto &component = Components[subjectType.getPointer()]; + assert(!component.ConcreteType); + component.ConcreteType = concreteType; + return; + } + + case Symbol::Kind::ConcreteConformance: + // "Concrete conformance requirements" are not recorded in the generic + // signature. + return; + + case Symbol::Kind::Name: + case Symbol::Kind::AssociatedType: + case Symbol::Kind::GenericParam: + break; + } + + llvm_unreachable("Invalid symbol kind"); + } + + assert(rule.getLHS().back().getKind() != Symbol::Kind::Protocol); + auto constraintType = Map.getTypeForTerm(rule.getLHS(), GenericParams); + auto subjectType = Map.getTypeForTerm(rule.getRHS(), GenericParams); + + Components[subjectType.getPointer()].Members.push_back(constraintType); + }; + + if (Debug) { + llvm::dbgs() << "\nMinimized rules:\n"; + } + + // Build the list of requirements, storing same-type requirements off + // to the side. + for (unsigned ruleID : rules) { + const auto &rule = System.getRule(ruleID); + + if (Debug) { + llvm::dbgs() << "- " << rule << "\n"; + } + + createRequirementFromRule(rule); + } +} + +void RequirementBuilder::addTypeAliasRules(ArrayRef rules) { + for (unsigned ruleID : rules) { + const auto &rule = System.getRule(ruleID); + auto name = *rule.isProtocolTypeAliasRule(); + Type underlyingType; + + auto subjectType = Map.getTypeForTerm(rule.getRHS(), GenericParams); + + if (auto prop = rule.isPropertyRule()) { + assert(prop->getKind() == Symbol::Kind::ConcreteType); + + // Requirements containing unresolved name symbols originate from + // invalid code and should not appear in the generic signature. + for (auto term : prop->getSubstitutions()) { + if (term.containsUnresolvedSymbols()) + continue; + } + + // Requirements containing error types originate from invalid code + // and should not appear in the generic signature. + if (prop->getConcreteType()->hasError()) + continue; + + auto concreteType = Map.getTypeFromSubstitutionSchema( + prop->getConcreteType(), + prop->getSubstitutions(), + GenericParams, MutableTerm()); + auto &component = Components[subjectType.getPointer()]; + assert(!component.ConcreteType); + Components[subjectType.getPointer()].ConcreteType = concreteType; + } else { + Components[subjectType.getPointer()].Aliases.push_back(name); + } + } +} + +void RequirementBuilder::processConnectedComponents() { + // Now, convert each connected component into a series of same-type + // requirements. + for (auto &pair : Components) { + pair.second.buildRequirements(pair.first, Reqs, Aliases); + } +} + +void RequirementBuilder::sortRequirements() { + llvm::array_pod_sort(Reqs.begin(), Reqs.end(), + [](const Requirement *lhs, const Requirement *rhs) -> int { + return lhs->compare(*rhs); + }); + + if (Debug) { + llvm::dbgs() << "Requirements:\n"; + for (const auto &req : Reqs) { + req.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + } + } +} + +void RequirementBuilder::sortTypeAliases() { + llvm::array_pod_sort(Aliases.begin(), Aliases.end(), + [](const ProtocolTypeAlias *lhs, + const ProtocolTypeAlias *rhs) -> int { + return lhs->getName().compare(rhs->getName()); + }); + + if (Debug) { + llvm::dbgs() << "\nMinimized type aliases:\n"; + for (const auto &alias : Aliases) { + PrintOptions opts; + opts.ProtocolQualifiedDependentMemberTypes = true; + + llvm::dbgs() << "- " << alias.getName() << " == "; + alias.getUnderlyingType().print(llvm::dbgs(), opts); + llvm::dbgs() << "\n"; + } + } +} + +/// Convert a list of non-permanent, non-redundant rewrite rules into a list of +/// requirements sorted in canonical order. The \p genericParams are used to +/// produce sugared types. +void +RequirementMachine::buildRequirementsFromRules( + ArrayRef requirementRules, + ArrayRef typeAliasRules, + TypeArrayView genericParams, + std::vector &reqs, + std::vector &aliases) const { + RequirementBuilder builder(System, Map, genericParams); + + builder.addRequirementRules(requirementRules); + builder.addTypeAliasRules(typeAliasRules); + builder.processConnectedComponents(); + builder.sortRequirements(); + builder.sortTypeAliases(); + + reqs = std::move(builder.Reqs); + aliases = std::move(builder.Aliases); +} diff --git a/lib/AST/RequirementMachine/RequirementMachine.h b/lib/AST/RequirementMachine/RequirementMachine.h index 1dd61075fc371..60b8de80c8720 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.h +++ b/lib/AST/RequirementMachine/RequirementMachine.h @@ -108,13 +108,12 @@ class RequirementMachine final { MutableTerm getLongestValidPrefix(const MutableTerm &term) const; - std::vector buildRequirementsFromRules( - ArrayRef rules, - TypeArrayView genericParams) const; - - std::vector buildProtocolTypeAliasesFromRules( - ArrayRef rules, - TypeArrayView genericParams) const; + void buildRequirementsFromRules( + ArrayRef requirementRules, + ArrayRef typeAliasRules, + TypeArrayView genericParams, + std::vector &reqs, + std::vector &aliases) const; TypeArrayView getGenericParams() const { return TypeArrayView( diff --git a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp index 7f66ffc00de79..10d8833eaff11 100644 --- a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp +++ b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp @@ -1,4 +1,4 @@ -//===--- RequirementMachineRequests.cpp -----------------------------------===// +//===--- RequirementMachineRequests.cpp - Request evaluator requests ------===// // // This source file is part of the Swift.org open source project // @@ -29,269 +29,13 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/AST/TypeRepr.h" #include "swift/Basic/Statistic.h" -#include "RequirementLowering.h" #include #include +#include "RequirementLowering.h" using namespace swift; using namespace rewriting; -namespace { - -/// Represents a set of types related by same-type requirements, and an -/// optional concrete type requirement. -struct ConnectedComponent { - llvm::SmallVector Members; - Type ConcreteType; - - void buildRequirements(Type subjectType, std::vector &reqs); -}; - -/// Case 1: A set of rewrite rules of the form: -/// -/// B => A -/// C => A -/// D => A -/// -/// Become a series of same-type requirements -/// -/// A == B, B == C, C == D -/// -/// Case 2: A set of rewrite rules of the form: -/// -/// A.[concrete: X] => A -/// B => A -/// C => A -/// D => A -/// -/// Become a series of same-type requirements -/// -/// A == X, B == X, C == X, D == X -void ConnectedComponent::buildRequirements(Type subjectType, - std::vector &reqs) { - std::sort(Members.begin(), Members.end(), - [](Type first, Type second) -> bool { - return compareDependentTypes(first, second) < 0; - }); - - if (!ConcreteType) { - for (auto constraintType : Members) { - reqs.emplace_back(RequirementKind::SameType, - subjectType, constraintType); - subjectType = constraintType; - } - } else if (!ConcreteType->hasError()) { - // For compatibility with the old GenericSignatureBuilder, drop requirements - // containing ErrorTypes. - reqs.emplace_back(RequirementKind::SameType, - subjectType, ConcreteType); - - for (auto constraintType : Members) { - reqs.emplace_back(RequirementKind::SameType, - constraintType, ConcreteType); - } - } -} - -} // end namespace - -/// Convert a list of non-permanent, non-redundant rewrite rules into a list of -/// requirements sorted in canonical order. The \p genericParams are used to -/// produce sugared types. -std::vector -RequirementMachine::buildRequirementsFromRules( - ArrayRef rules, - TypeArrayView genericParams) const { - std::vector reqs; - llvm::SmallDenseMap sameTypeReqs; - - // Convert a rewrite rule into a requirement. - auto createRequirementFromRule = [&](const Rule &rule) { - if (auto prop = rule.isPropertyRule()) { - auto subjectType = Map.getTypeForTerm(rule.getRHS(), genericParams); - - switch (prop->getKind()) { - case Symbol::Kind::Protocol: - reqs.emplace_back(RequirementKind::Conformance, - subjectType, - prop->getProtocol()->getDeclaredInterfaceType()); - return; - - case Symbol::Kind::Layout: - reqs.emplace_back(RequirementKind::Layout, - subjectType, - prop->getLayoutConstraint()); - return; - - case Symbol::Kind::Superclass: { - // Requirements containing unresolved name symbols originate from - // invalid code and should not appear in the generic signature. - for (auto term : prop->getSubstitutions()) { - if (term.containsUnresolvedSymbols()) - return; - } - - // Requirements containing error types originate from invalid code - // and should not appear in the generic signature. - if (prop->getConcreteType()->hasError()) - return; - - auto superclassType = Map.getTypeFromSubstitutionSchema( - prop->getConcreteType(), - prop->getSubstitutions(), - genericParams, MutableTerm()); - - reqs.emplace_back(RequirementKind::Superclass, - subjectType, superclassType); - return; - } - - case Symbol::Kind::ConcreteType: { - // Requirements containing unresolved name symbols originate from - // invalid code and should not appear in the generic signature. - for (auto term : prop->getSubstitutions()) { - if (term.containsUnresolvedSymbols()) - return; - } - - // Requirements containing error types originate from invalid code - // and should not appear in the generic signature. - if (prop->getConcreteType()->hasError()) - return; - - auto concreteType = Map.getTypeFromSubstitutionSchema( - prop->getConcreteType(), - prop->getSubstitutions(), - genericParams, MutableTerm()); - - auto &component = sameTypeReqs[subjectType.getPointer()]; - assert(!component.ConcreteType); - component.ConcreteType = concreteType; - return; - } - - case Symbol::Kind::ConcreteConformance: - // "Concrete conformance requirements" are not recorded in the generic - // signature. - return; - - case Symbol::Kind::Name: - case Symbol::Kind::AssociatedType: - case Symbol::Kind::GenericParam: - break; - } - - llvm_unreachable("Invalid symbol kind"); - } - - assert(rule.getLHS().back().getKind() != Symbol::Kind::Protocol); - auto constraintType = Map.getTypeForTerm(rule.getLHS(), genericParams); - auto subjectType = Map.getTypeForTerm(rule.getRHS(), genericParams); - - sameTypeReqs[subjectType.getPointer()].Members.push_back(constraintType); - }; - - if (getDebugOptions().contains(DebugFlags::Minimization)) { - llvm::dbgs() << "\nMinimized rules:\n"; - } - - // Build the list of requirements, storing same-type requirements off - // to the side. - for (unsigned ruleID : rules) { - const auto &rule = System.getRule(ruleID); - - if (getDebugOptions().contains(DebugFlags::Minimization)) { - llvm::dbgs() << "- " << rule << "\n"; - } - - createRequirementFromRule(rule); - } - - // Now, convert each connected component into a series of same-type - // requirements. - for (auto &pair : sameTypeReqs) { - pair.second.buildRequirements(pair.first, reqs); - } - - if (getDebugOptions().contains(DebugFlags::Minimization)) { - llvm::dbgs() << "Requirements:\n"; - for (const auto &req : reqs) { - req.dump(llvm::dbgs()); - llvm::dbgs() << "\n"; - } - } - - // Finally, sort the requirements in canonical order. - llvm::array_pod_sort(reqs.begin(), reqs.end(), - [](const Requirement *lhs, const Requirement *rhs) -> int { - return lhs->compare(*rhs); - }); - - return reqs; -} - -/// Convert a list of protocol typealias rules to a list of name/underlying type -/// pairs. -std::vector -RequirementMachine::buildProtocolTypeAliasesFromRules( - ArrayRef rules, - TypeArrayView genericParams) const { - std::vector aliases; - - if (getDebugOptions().contains(DebugFlags::Minimization)) { - llvm::dbgs() << "\nMinimized type aliases:\n"; - } - - for (unsigned ruleID : rules) { - const auto &rule = System.getRule(ruleID); - auto name = *rule.isProtocolTypeAliasRule(); - Type underlyingType; - - if (auto prop = rule.isPropertyRule()) { - assert(prop->getKind() == Symbol::Kind::ConcreteType); - - // Requirements containing unresolved name symbols originate from - // invalid code and should not appear in the generic signature. - for (auto term : prop->getSubstitutions()) { - if (term.containsUnresolvedSymbols()) - continue; - } - - // Requirements containing error types originate from invalid code - // and should not appear in the generic signature. - if (prop->getConcreteType()->hasError()) - continue; - - underlyingType = Map.getTypeFromSubstitutionSchema( - prop->getConcreteType(), - prop->getSubstitutions(), - genericParams, MutableTerm()); - } else { - underlyingType = Map.getTypeForTerm(rule.getRHS(), genericParams); - } - - aliases.emplace_back(name, underlyingType); - - if (getDebugOptions().contains(DebugFlags::Minimization)) { - PrintOptions opts; - opts.ProtocolQualifiedDependentMemberTypes = true; - - llvm::dbgs() << "- " << name << " == "; - underlyingType.print(llvm::dbgs(), opts); - llvm::dbgs() << "\n"; - } - } - - // Finally, sort the aliases in canonical order. - llvm::array_pod_sort(aliases.begin(), aliases.end(), - [](const ProtocolTypeAlias *lhs, - const ProtocolTypeAlias *rhs) -> int { - return lhs->getName().compare(rhs->getName()); - }); - - return aliases; -} - /// Builds the requirement signatures for each protocol in this strongly /// connected component. llvm::DenseMap @@ -322,14 +66,16 @@ RequirementMachine::computeMinimalProtocolRequirements() { auto genericParams = proto->getGenericSignature().getGenericParams(); const auto &entry = rules[proto]; - auto reqs = ctx.AllocateCopy( - buildRequirementsFromRules(entry.Requirements, - genericParams)); - auto aliases = ctx.AllocateCopy( - buildProtocolTypeAliasesFromRules(entry.TypeAliases, - genericParams)); - - result[proto] = RequirementSignature(reqs, aliases); + + std::vector reqs; + std::vector aliases; + buildRequirementsFromRules(entry.Requirements, + entry.TypeAliases, + genericParams, + reqs, aliases); + + result[proto] = RequirementSignature(ctx.AllocateCopy(reqs), + ctx.AllocateCopy(aliases)); } return result; @@ -448,7 +194,15 @@ RequirementMachine::computeMinimalGenericSignatureRequirements() { } auto rules = System.getMinimizedGenericSignatureRules(); - return buildRequirementsFromRules(rules, getGenericParams()); + + std::vector reqs; + std::vector aliases; + + buildRequirementsFromRules(rules, ArrayRef(), getGenericParams(), + reqs, aliases); + assert(aliases.empty()); + + return reqs; } GenericSignatureWithError From 762bf1e7d0857545d538d2c69f81c6d48d0b0968 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 11 Mar 2022 21:20:21 -0500 Subject: [PATCH 028/242] RequirementMachine: Refine 'derived via protocol typealias' criterion Preserves concrete type rules on associated types that were derived from rules indirectly formed from protocol typealias rules. That is, if you have a pair of rules in another minimization domain: [P].A.[concrete: C] => [P].A [Q:T].[P] => [Q:T] Then completion will introduce a new rule: [Q:T].A.[concrete: C] => [Q:T].A Since this rule is outside of our minimization domain, we don't record a rewrite loop for it, and it will never become redundant. Now if we have a rule in our own minimization domain: T.[Q:T].A => T.[Q:U] Then we get a new rule: T.[Q:U].[concrete: C] => T.[Q:U] Make sure we keep this rule around on account of it being derived from ([Q:T].A.[concrete: C] => [Q:T].A). --- .../RequirementMachine/HomotopyReduction.cpp | 3 +-- lib/AST/RequirementMachine/RewriteSystem.cpp | 23 +++++++++++++++++++ lib/AST/RequirementMachine/RewriteSystem.h | 2 ++ test/Generics/protocol_type_aliases.swift | 4 ++-- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/AST/RequirementMachine/HomotopyReduction.cpp b/lib/AST/RequirementMachine/HomotopyReduction.cpp index cf5ec036bbdfc..190d24198a290 100644 --- a/lib/AST/RequirementMachine/HomotopyReduction.cpp +++ b/lib/AST/RequirementMachine/HomotopyReduction.cpp @@ -83,8 +83,7 @@ void RewriteLoop::recompute(const RewriteSystem &system) { Useful |= (!step.isInContext() && !evaluator.isInContext()); const auto &rule = system.getRule(step.getRuleID()); - if (rule.isProtocolTypeAliasRule() && - rule.getLHS().size() == 3) + if (rule.isDerivedFromConcreteProtocolTypeAliasRule()) HasConcreteTypeAliasRule = 1; break; diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp index aea7be104c992..6d2465cb7e9ab 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.cpp +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -193,6 +193,29 @@ Optional Rule::isProtocolTypeAliasRule() const { return LHS[1].getName(); } +/// A rule was derived from a concrete protocol typealias if it +/// takes the following form: +/// +/// T.A.[concrete: C] => T.A +/// +/// Where the prefix term T does not contain any name symbols, and +/// A is a name symbol. +bool Rule::isDerivedFromConcreteProtocolTypeAliasRule() const { + auto optSymbol = isPropertyRule(); + if (!optSymbol || optSymbol->getKind() != Symbol::Kind::ConcreteType) + return false; + + for (unsigned i = 0, e = RHS.size() - 1; i < e; ++i) { + if (RHS[i].getKind() == Symbol::Kind::Name) + return false; + } + + if (RHS.back().getKind() != Symbol::Kind::Name) + return false; + + return true; +} + /// Returns the length of the left hand side. unsigned Rule::getDepth() const { auto result = LHS.size(); diff --git a/lib/AST/RequirementMachine/RewriteSystem.h b/lib/AST/RequirementMachine/RewriteSystem.h index 9bb34d39cd8c0..bdfe21374ed12 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.h +++ b/lib/AST/RequirementMachine/RewriteSystem.h @@ -160,6 +160,8 @@ class Rule final { Optional isProtocolTypeAliasRule() const; + bool isDerivedFromConcreteProtocolTypeAliasRule() const; + void markLHSSimplified() { assert(!LHSSimplified); LHSSimplified = true; diff --git a/test/Generics/protocol_type_aliases.swift b/test/Generics/protocol_type_aliases.swift index f9bc514250ae7..3906cafac8ead 100644 --- a/test/Generics/protocol_type_aliases.swift +++ b/test/Generics/protocol_type_aliases.swift @@ -1,5 +1,5 @@ -// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=verify -// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=verify > %t.dump 2>&1 +// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=verify -requirement-machine-inferred-signatures=verify +// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=verify -requirement-machine-inferred-signatures=verify > %t.dump 2>&1 func sameType(_: T.Type, _: T.Type) {} From e0c10d412ed4336cf0fc18e34f340f3cc1a7f07f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 11 Mar 2022 21:20:35 -0500 Subject: [PATCH 029/242] RequirementMachine: Tweak debug output --- lib/AST/RequirementMachine/RewriteSystem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp index 6d2465cb7e9ab..55b00ff165478 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.cpp +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -864,10 +864,11 @@ void RewriteSystem::dump(llvm::raw_ostream &out) const { out << "- " << rule; if (auto ID = rule.getRequirementID()) { auto requirement = WrittenRequirements[*ID]; - out << ", ID: " << *ID << ", "; + out << " [ID: " << *ID << " - "; requirement.req.dump(out); out << " at "; requirement.loc.print(out, Context.getASTContext().SourceMgr); + out << "]"; } out << "\n"; } From 05aeeff3867ef973717d9577527f1169a94a5b50 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 11 Mar 2022 22:54:48 -0500 Subject: [PATCH 030/242] RequirementMachine: Reconstitute sugar in trivial cases The Requirement Machine operates on canonical types internally and erases sugared types appearing in generic requirements as a result. For trivial cases like Array vs [T], we can use the existing TypeBase::reconstituteSugar() utility to produce a more aesthetically-pleasing generic signature. --- .../RequirementMachine/RequirementBuilder.cpp | 27 ++++++++--- .../RequirementMachine/RequirementMachine.h | 4 +- .../RequirementMachineRequests.cpp | 14 ++++-- test/Constraints/rdar39931339.swift | 5 ++ ...al_requirement_inference_in_protocol.swift | 4 +- ...ize_superclass_unification_generic_2.swift | 36 +++++++------- test/Generics/reconstitute_sugar.swift | 47 +++++++++++++++++++ 7 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 test/Generics/reconstitute_sugar.swift diff --git a/lib/AST/RequirementMachine/RequirementBuilder.cpp b/lib/AST/RequirementMachine/RequirementBuilder.cpp index eb08ab3aaeb33..a6de5abc8ce31 100644 --- a/lib/AST/RequirementMachine/RequirementBuilder.cpp +++ b/lib/AST/RequirementMachine/RequirementBuilder.cpp @@ -127,6 +127,7 @@ class RequirementBuilder { const RewriteSystem &System; const PropertyMap ⤅ TypeArrayView GenericParams; + bool ReconstituteSugar; bool Debug; // Temporary state populated by addRequirementRules() and @@ -139,8 +140,11 @@ class RequirementBuilder { std::vector Aliases; RequirementBuilder(const RewriteSystem &system, const PropertyMap &map, - TypeArrayView genericParams) - : System(system), Map(map), GenericParams(genericParams), + TypeArrayView genericParams, + bool reconstituteSugar) + : System(system), Map(map), + GenericParams(genericParams), + ReconstituteSugar(reconstituteSugar), Debug(System.getDebugOptions().contains(DebugFlags::Minimization)) {} void addRequirementRules(ArrayRef rules); @@ -186,11 +190,14 @@ void RequirementBuilder::addRequirementRules(ArrayRef rules) { if (prop->getConcreteType()->hasError()) return; - auto superclassType = Map.getTypeFromSubstitutionSchema( + Type superclassType = Map.getTypeFromSubstitutionSchema( prop->getConcreteType(), prop->getSubstitutions(), GenericParams, MutableTerm()); + if (ReconstituteSugar) + superclassType = superclassType->reconstituteSugar(/*recursive=*/true); + Reqs.emplace_back(RequirementKind::Superclass, subjectType, superclassType); return; @@ -209,11 +216,14 @@ void RequirementBuilder::addRequirementRules(ArrayRef rules) { if (prop->getConcreteType()->hasError()) return; - auto concreteType = Map.getTypeFromSubstitutionSchema( + Type concreteType = Map.getTypeFromSubstitutionSchema( prop->getConcreteType(), prop->getSubstitutions(), GenericParams, MutableTerm()); + if (ReconstituteSugar) + concreteType = concreteType->reconstituteSugar(/*recursive=*/true); + auto &component = Components[subjectType.getPointer()]; assert(!component.ConcreteType); component.ConcreteType = concreteType; @@ -281,10 +291,14 @@ void RequirementBuilder::addTypeAliasRules(ArrayRef rules) { if (prop->getConcreteType()->hasError()) continue; - auto concreteType = Map.getTypeFromSubstitutionSchema( + Type concreteType = Map.getTypeFromSubstitutionSchema( prop->getConcreteType(), prop->getSubstitutions(), GenericParams, MutableTerm()); + + if (ReconstituteSugar) + concreteType = concreteType->reconstituteSugar(/*recursive=*/true); + auto &component = Components[subjectType.getPointer()]; assert(!component.ConcreteType); Components[subjectType.getPointer()].ConcreteType = concreteType; @@ -345,9 +359,10 @@ RequirementMachine::buildRequirementsFromRules( ArrayRef requirementRules, ArrayRef typeAliasRules, TypeArrayView genericParams, + bool reconstituteSugar, std::vector &reqs, std::vector &aliases) const { - RequirementBuilder builder(System, Map, genericParams); + RequirementBuilder builder(System, Map, genericParams, reconstituteSugar); builder.addRequirementRules(requirementRules); builder.addTypeAliasRules(typeAliasRules); diff --git a/lib/AST/RequirementMachine/RequirementMachine.h b/lib/AST/RequirementMachine/RequirementMachine.h index 60b8de80c8720..743e54f1ede1d 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.h +++ b/lib/AST/RequirementMachine/RequirementMachine.h @@ -112,6 +112,7 @@ class RequirementMachine final { ArrayRef requirementRules, ArrayRef typeAliasRules, TypeArrayView genericParams, + bool reconstituteSugar, std::vector &reqs, std::vector &aliases) const; @@ -149,7 +150,8 @@ class RequirementMachine final { llvm::DenseMap computeMinimalProtocolRequirements(); - std::vector computeMinimalGenericSignatureRequirements(); + std::vector + computeMinimalGenericSignatureRequirements(bool reconstituteSugar); std::string getRuleAsStringForDiagnostics(unsigned ruleID) const; diff --git a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp index 10d8833eaff11..933438ab1814a 100644 --- a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp +++ b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp @@ -72,6 +72,7 @@ RequirementMachine::computeMinimalProtocolRequirements() { buildRequirementsFromRules(entry.Requirements, entry.TypeAliases, genericParams, + /*reconstituteSugar=*/true, reqs, aliases); result[proto] = RequirementSignature(ctx.AllocateCopy(reqs), @@ -180,7 +181,8 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator, /// Builds the top-level generic signature requirements for this rewrite system. std::vector -RequirementMachine::computeMinimalGenericSignatureRequirements() { +RequirementMachine::computeMinimalGenericSignatureRequirements( + bool reconstituteSugar) { assert(System.getProtocols().empty() && "Not a top-level generic signature rewrite system"); assert(!Params.empty() && @@ -199,7 +201,7 @@ RequirementMachine::computeMinimalGenericSignatureRequirements() { std::vector aliases; buildRequirementsFromRules(rules, ArrayRef(), getGenericParams(), - reqs, aliases); + reconstituteSugar, reqs, aliases); assert(aliases.empty()); return reqs; @@ -281,8 +283,11 @@ AbstractGenericSignatureRequestRQM::evaluate( machine->initWithWrittenRequirements(genericParams, requirements); machine->checkCompletionResult(status.first); + // We pass reconstituteSugar=false to ensure that if the original + // requirements were canonical, the final signature remains canonical. auto minimalRequirements = - machine->computeMinimalGenericSignatureRequirements(); + machine->computeMinimalGenericSignatureRequirements( + /*reconstituteSugar=*/false); auto result = GenericSignature::get(genericParams, minimalRequirements); bool hadError = machine->hadError(); @@ -423,7 +428,8 @@ InferredGenericSignatureRequestRQM::evaluate( } auto minimalRequirements = - machine->computeMinimalGenericSignatureRequirements(); + machine->computeMinimalGenericSignatureRequirements( + /*reconstituteSugar=*/true); auto result = GenericSignature::get(genericParams, minimalRequirements); bool hadError = machine->hadError(); diff --git a/test/Constraints/rdar39931339.swift b/test/Constraints/rdar39931339.swift index eee8de950baad..58dc052f2ec87 100644 --- a/test/Constraints/rdar39931339.swift +++ b/test/Constraints/rdar39931339.swift @@ -1,4 +1,5 @@ // RUN: %target-typecheck-verify-swift +// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s protocol P {} @@ -17,10 +18,14 @@ extension S where T == Float { // expected-note {{requirement specified as 'T' = class A {} +// CHECK-LABEL: ExtensionDecl line={{.*}} base=A +// CHECK-NEXT: Generic signature: extension A where T == [U], U: P { typealias S1 = Int } +// CHECK-LABEL: ExtensionDecl line={{.*}} base=A +// CHECK-NEXT: Generic signature: extension A where T == [U], U == Int { // expected-note@-1 {{requirement specified as 'T' == '[Int]' [with T = [String]]}} typealias S2 = Int diff --git a/test/Generics/conditional_requirement_inference_in_protocol.swift b/test/Generics/conditional_requirement_inference_in_protocol.swift index ac43055af09ea..e33037a844f95 100644 --- a/test/Generics/conditional_requirement_inference_in_protocol.swift +++ b/test/Generics/conditional_requirement_inference_in_protocol.swift @@ -2,7 +2,7 @@ // RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures 2>&1 | %FileCheck %s // CHECK-LABEL: conditional_requirement_inference_in_protocol.(file).Good@ -// CHECK-LABEL: Requirement signature: , Self.[Good]U : Equatable> +// CHECK-LABEL: Requirement signature: protocol Good { associatedtype T : Equatable // expected-warning {{redundant conformance constraint 'Self.T' : 'Equatable'}} @@ -10,7 +10,7 @@ protocol Good { } // CHECK-LABEL: conditional_requirement_inference_in_protocol.(file).Bad@ -// CHECK-LABEL: Requirement signature: > +// CHECK-LABEL: Requirement signature: protocol Bad { associatedtype T : Equatable // expected-warning {{redundant conformance constraint 'Self.T' : 'Equatable'}} diff --git a/test/Generics/minimize_superclass_unification_generic_2.swift b/test/Generics/minimize_superclass_unification_generic_2.swift index 47dc5c947c00d..f85ee442cb794 100644 --- a/test/Generics/minimize_superclass_unification_generic_2.swift +++ b/test/Generics/minimize_superclass_unification_generic_2.swift @@ -59,7 +59,7 @@ protocol Pba { // CHECK-NEXT: Requirement signature: Array, +// CHECK-SAME: Self.[Pac]A2 == (Self.[Pac]C1) -> [Self.[Pac]C2.[Sequence]Element], // CHECK-SAME: Self.[Pac]A3 == Int, // CHECK-SAME: Self.[Pac]C2 : Sequence, @@ -82,7 +82,7 @@ protocol Pac { // CHECK-NEXT: Requirement signature: Array, +// CHECK-SAME: Self.[Pca]A2 == (Self.[Pca]C1) -> [Self.[Pca]C2.[Sequence]Element], // CHECK-SAME: Self.[Pca]A3 == Int, // CHECK-SAME: Self.[Pca]C2 : Sequence, @@ -106,7 +106,7 @@ protocol Pca { // CHECK-SAME: Self.[Pbc]B1 == String, // CHECK-SAME: Self.[Pbc]B2 == Self.[Pbc]C1, -// CHECK-SAME: Self.[Pbc]B3 == Array, +// CHECK-SAME: Self.[Pbc]B3 == [Self.[Pbc]C2.[Sequence]Element], // CHECK-SAME: Self.[Pbc]B4 == Self.[Pbc]C3, // CHECK-SAME: Self.[Pbc]C2 : Sequence, @@ -131,7 +131,7 @@ protocol Pbc { // CHECK-SAME: Self.[Pcb]B1 == String, // CHECK-SAME: Self.[Pcb]B2 == Self.[Pcb]C1, -// CHECK-SAME: Self.[Pcb]B3 == Array, +// CHECK-SAME: Self.[Pcb]B3 == [Self.[Pcb]C2.[Sequence]Element], // CHECK-SAME: Self.[Pcb]B4 == Self.[Pcb]C3, // CHECK-SAME: Self.[Pcb]C2 : Sequence, @@ -160,12 +160,12 @@ protocol Pcb { // CHECK-NEXT: Requirement signature: Array, +// CHECK-SAME: Self.[Pabc]A2 == (Self.[Pabc]B2) -> [Self.[Pabc]C2.[Sequence]Element], // CHECK-SAME: Self.[Pabc]A3 == Int, // CHECK-SAME: Self.[Pabc]B1 == String, // CHECK-SAME: Self.[Pabc]B2 == Self.[Pabc]C1, -// CHECK-SAME: Self.[Pabc]B3 == Array, +// CHECK-SAME: Self.[Pabc]B3 == [Self.[Pabc]C2.[Sequence]Element], // CHECK-SAME: Self.[Pabc]B4 == Self.[Pabc]C3, // CHECK-SAME: Self.[Pabc]C2 : Sequence, @@ -193,12 +193,12 @@ protocol Pabc { // CHECK-NEXT: Requirement signature: Array, +// CHECK-SAME: Self.[Pacb]A2 == (Self.[Pacb]B2) -> [Self.[Pacb]C2.[Sequence]Element], // CHECK-SAME: Self.[Pacb]A3 == Int, // CHECK-SAME: Self.[Pacb]B1 == String, // CHECK-SAME: Self.[Pacb]B2 == Self.[Pacb]C1, -// CHECK-SAME: Self.[Pacb]B3 == Array, +// CHECK-SAME: Self.[Pacb]B3 == [Self.[Pacb]C2.[Sequence]Element], // CHECK-SAME: Self.[Pacb]B4 == Self.[Pacb]C3, // CHECK-SAME: Self.[Pacb]C2 : Sequence, @@ -226,12 +226,12 @@ protocol Pacb { // CHECK-NEXT: Requirement signature: Array, +// CHECK-SAME: Self.[Pbac]A2 == (Self.[Pbac]B2) -> [Self.[Pbac]C2.[Sequence]Element], // CHECK-SAME: Self.[Pbac]A3 == Int, // CHECK-SAME: Self.[Pbac]B1 == String, // CHECK-SAME: Self.[Pbac]B2 == Self.[Pbac]C1, -// CHECK-SAME: Self.[Pbac]B3 == Array, +// CHECK-SAME: Self.[Pbac]B3 == [Self.[Pbac]C2.[Sequence]Element], // CHECK-SAME: Self.[Pbac]B4 == Self.[Pbac]C3, // CHECK-SAME: Self.[Pbac]C2 : Sequence, @@ -259,12 +259,12 @@ protocol Pbac { // CHECK-NEXT: Requirement signature: Array, +// CHECK-SAME: Self.[Pbca]A2 == (Self.[Pbca]B2) -> [Self.[Pbca]C2.[Sequence]Element], // CHECK-SAME: Self.[Pbca]A3 == Int, // CHECK-SAME: Self.[Pbca]B1 == String, // CHECK-SAME: Self.[Pbca]B2 == Self.[Pbca]C1, -// CHECK-SAME: Self.[Pbca]B3 == Array, +// CHECK-SAME: Self.[Pbca]B3 == [Self.[Pbca]C2.[Sequence]Element], // CHECK-SAME: Self.[Pbca]B4 == Self.[Pbca]C3, // CHECK-SAME: Self.[Pbca]C2 : Sequence, @@ -292,12 +292,12 @@ protocol Pbca { // CHECK-NEXT: Requirement signature: Array, +// CHECK-SAME: Self.[Pcab]A2 == (Self.[Pcab]B2) -> [Self.[Pcab]C2.[Sequence]Element], // CHECK-SAME: Self.[Pcab]A3 == Int, // CHECK-SAME: Self.[Pcab]B1 == String, // CHECK-SAME: Self.[Pcab]B2 == Self.[Pcab]C1, -// CHECK-SAME: Self.[Pcab]B3 == Array, +// CHECK-SAME: Self.[Pcab]B3 == [Self.[Pcab]C2.[Sequence]Element], // CHECK-SAME: Self.[Pcab]B4 == Self.[Pcab]C3, // CHECK-SAME: Self.[Pcab]C2 : Sequence, @@ -325,12 +325,12 @@ protocol Pcab { // CHECK-NEXT: Requirement signature: Array, +// CHECK-SAME: Self.[Pcba]A2 == (Self.[Pcba]B2) -> [Self.[Pcba]C2.[Sequence]Element], // CHECK-SAME: Self.[Pcba]A3 == Int, // CHECK-SAME: Self.[Pcba]B1 == String, // CHECK-SAME: Self.[Pcba]B2 == Self.[Pcba]C1, -// CHECK-SAME: Self.[Pcba]B3 == Array, +// CHECK-SAME: Self.[Pcba]B3 == [Self.[Pcba]C2.[Sequence]Element], // CHECK-SAME: Self.[Pcba]B4 == Self.[Pcba]C3, // CHECK-SAME: Self.[Pcba]C2 : Sequence, @@ -438,7 +438,7 @@ protocol PaQc { // CHECK-NEXT: Requirement signature: Array, +// CHECK-SAME: Self.[PcQa]A2 == (Self.[PcQa]T.[Pc]C1) -> [Self.[PcQa]T.[Pc]C2.[Sequence]Element], // CHECK-SAME: Self.[PcQa]A3 == Int, // CHECK-SAME: Self.[PcQa]T : Pc> @@ -473,7 +473,7 @@ protocol PbQc { // CHECK-SAME: Self.[PcQb]B1 == String, // CHECK-SAME: Self.[PcQb]B2 == Self.[PcQb]T.[Pc]C1, -// CHECK-SAME: Self.[PcQb]B3 == Array, +// CHECK-SAME: Self.[PcQb]B3 == [Self.[PcQb]T.[Pc]C2.[Sequence]Element], // CHECK-SAME: Self.[PcQb]B4 == Self.[PcQb]T.[Pc]C3, // CHECK-SAME: Self.[PcQb]T : Pc> diff --git a/test/Generics/reconstitute_sugar.swift b/test/Generics/reconstitute_sugar.swift new file mode 100644 index 0000000000000..77158c184820d --- /dev/null +++ b/test/Generics/reconstitute_sugar.swift @@ -0,0 +1,47 @@ +// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s + +// The Requirement Machine works with canonical types internally, so make sure +// we reconstitute sugar in trivial cases before surfacing the generic +// signature to the user. + +struct G {} + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=G +// CHECK-NEXT: Generic signature: +extension G where X == Optional {} + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=G +// CHECK-NEXT: Generic signature: +extension G where X == Array {} + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=G +// CHECK-NEXT: Generic signature: +extension G where X == Dictionary {} + +// We don't do () => Swift.Void. + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=G +// CHECK-NEXT: Generic signature: +extension G where X == () {} + +// Now make sure we do the same for superclass requirements. + +class C {} + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=G +// CHECK-NEXT: Generic signature: > +extension G where X : C> {} + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=G +// CHECK-NEXT: Generic signature: > +extension G where X : C> {} + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=G +// CHECK-NEXT: Generic signature: , Y : Hashable> +extension G where X : C> {} + +// We don't do () => Swift.Void. + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=G +// CHECK-NEXT: Generic signature: > +extension G where X : C<()> {} From 1fc998a715a664169a05237af5e08dcad774a2ea Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 13 Mar 2022 17:25:16 -0400 Subject: [PATCH 031/242] RequirementMachine: Verify protocol requirement signatures even in noassert builds --- lib/Sema/TypeCheckDeclPrimary.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index d2fbe1060194e..2eb45ab44adab 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2690,12 +2690,20 @@ class DeclChecker : public DeclVisitor { llvm::errs() << "\n"; } - -#ifndef NDEBUG - // In asserts builds, also verify some invariants of the requirement - // signature. - PD->getGenericSignature().verify(reqSig); -#endif + if (getASTContext().LangOpts.RequirementMachineProtocolSignatures == + RequirementMachineMode::Disabled) { + #ifndef NDEBUG + // The GenericSignatureBuilder outputs incorrectly-minimized signatures + // sometimes, so only check invariants in asserts builds. + PD->getGenericSignature().verify(reqSig); + #endif + } else { + // When using the Requirement Machine, always verify signatures. + // An incorrect signature indicates a serious problem which can cause + // miscompiles or inadvertent ABI dependencies on compiler bugs, so + // we really want to avoid letting one slip by. + PD->getGenericSignature().verify(reqSig); + } (void) reqSig; From 1578ab578a6fd52c65d2d04c30c982f6bffd8a66 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 13 Mar 2022 18:31:08 -0400 Subject: [PATCH 032/242] RequirementMachine: Refactor requests to not be downstream of the GenericSignatureBuilder This will make it easier to remove the GenericSignatureBuilder (which still won't happen for a little while). --- include/swift/AST/Decl.h | 1 + include/swift/AST/TypeCheckRequests.h | 94 ++++ include/swift/AST/TypeCheckerTypeIDZone.def | 16 + lib/AST/GenericSignatureBuilder.cpp | 498 ++++++++++-------- .../RequirementMachineRequests.cpp | 102 +++- lib/AST/TypeCheckRequests.cpp | 8 + 6 files changed, 498 insertions(+), 221 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index a265f9a5ddf14..91ae1164268bb 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -4389,6 +4389,7 @@ class ProtocolDecl final : public NominalTypeDecl { friend class ProtocolDependenciesRequest; friend class RequirementSignatureRequest; friend class RequirementSignatureRequestRQM; + friend class RequirementSignatureRequestGSB; friend class ProtocolRequiresClassRequest; friend class ExistentialConformsToSelfRequest; friend class ExistentialRequiresAnyRequest; diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 8b4f925ba21cf..644d4e1d228fa 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -493,6 +493,27 @@ class RequirementSignatureRequestRQM : bool isCached() const { return true; } }; +/// Compute a protocol's requirement signature using the GenericSignatureBuilder. +/// This is temporary; once the GenericSignatureBuilder goes away this will +/// be removed. +class RequirementSignatureRequestGSB : + public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + RequirementSignature + evaluate(Evaluator &evaluator, ProtocolDecl *proto) const; + +public: + bool isCached() const { return true; } +}; + /// Compute the requirements that describe a protocol. class RequirementSignatureRequest : public SimpleRequest, + SmallVector), + RequestFlags::Cached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + GenericSignatureWithError + evaluate(Evaluator &evaluator, + const GenericSignatureImpl *baseSignature, + SmallVector addedParameters, + SmallVector addedRequirements) const; + +public: + // Separate caching. + bool isCached() const { return true; } + + /// Abstract generic signature requests never have source-location info. + SourceLoc getNearestLoc() const { + return SourceLoc(); + } +}; + class InferredGenericSignatureRequest : public SimpleRequest, + SmallVector, + bool), + RequestFlags::Cached> { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + // Evaluation. + GenericSignatureWithError + evaluate(Evaluator &evaluator, + ModuleDecl *parentModule, + const GenericSignatureImpl *baseSignature, + GenericParamList *genericParams, + WhereClauseOwner whereClause, + SmallVector addedRequirements, + SmallVector inferenceSources, + bool allowConcreteGenericParams) const; + +public: + // Separate caching. + bool isCached() const { return true; } + + /// Inferred generic signature requests don't have source-location info. + SourceLoc getNearestLoc() const { + return SourceLoc(); + } + + // Cycle handling. + void noteCycleStep(DiagnosticEngine &diags) const; +}; + void simple_display(llvm::raw_ostream &out, const TypeLoc source); class ExtendedTypeRequest diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index f8e66b275c4a5..3fd6390dad523 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -25,6 +25,11 @@ SWIFT_REQUEST(TypeChecker, AbstractGenericSignatureRequestRQM, SmallVector, SmallVector), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, AbstractGenericSignatureRequestGSB, + GenericSignatureWithError (const GenericSignatureImpl *, + SmallVector, + SmallVector), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, ApplyAccessNoteRequest, evaluator::SideEffect(ValueDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, AttachedResultBuilderRequest, @@ -192,6 +197,14 @@ SWIFT_REQUEST(TypeChecker, InferredGenericSignatureRequestRQM, SmallVector, SmallVector, bool), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, InferredGenericSignatureRequestGSB, + GenericSignatureWithError (ModuleDecl *, + const GenericSignatureImpl *, + GenericParamList *, + WhereClauseOwner, + SmallVector, + SmallVector, bool), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, DistributedModuleIsAvailableRequest, bool(ModuleDecl *), Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, InheritedTypeRequest, @@ -288,6 +301,9 @@ SWIFT_REQUEST(TypeChecker, ProtocolDependenciesRequest, SWIFT_REQUEST(TypeChecker, RequirementSignatureRequestRQM, RequirementSignature(ProtocolDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, RequirementSignatureRequestGSB, + RequirementSignature(ProtocolDecl *), Cached, + NoLocationInfo) SWIFT_REQUEST(TypeChecker, RequirementSignatureRequest, RequirementSignature(ProtocolDecl *), SeparatelyCached, NoLocationInfo) diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index c1d1106a4ef9c..fa5f7a5e08bff 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -8224,6 +8224,79 @@ GenericSignature GenericSignatureBuilder::computeGenericSignature( return sig; } +GenericSignatureWithError +AbstractGenericSignatureRequest::evaluate( + Evaluator &evaluator, + const GenericSignatureImpl *baseSignatureImpl, + SmallVector addedParameters, + SmallVector addedRequirements) const { + GenericSignature baseSignature = GenericSignature{baseSignatureImpl}; + // If nothing is added to the base signature, just return the base + // signature. + if (addedParameters.empty() && addedRequirements.empty()) + return GenericSignatureWithError(baseSignature, /*hadError=*/false); + + ASTContext &ctx = addedParameters.empty() + ? addedRequirements.front().getFirstType()->getASTContext() + : addedParameters.front()->getASTContext(); + + auto buildViaGSB = [&]() { + return evaluateOrDefault( + ctx.evaluator, + AbstractGenericSignatureRequestGSB{ + baseSignatureImpl, + std::move(addedParameters), + std::move(addedRequirements)}, + GenericSignatureWithError()); + }; + + auto buildViaRQM = [&]() { + return evaluateOrDefault( + ctx.evaluator, + AbstractGenericSignatureRequestRQM{ + baseSignatureImpl, + std::move(addedParameters), + std::move(addedRequirements)}, + GenericSignatureWithError()); + }; + + switch (ctx.LangOpts.RequirementMachineAbstractSignatures) { + case RequirementMachineMode::Disabled: + return buildViaGSB(); + + case RequirementMachineMode::Enabled: + return buildViaRQM(); + + case RequirementMachineMode::Verify: + case RequirementMachineMode::Check: { + auto rqmResult = buildViaRQM(); + auto gsbResult = buildViaGSB(); + + if (!rqmResult.getPointer() && !gsbResult.getPointer()) + return rqmResult; + + if (!rqmResult.getPointer()->isEqual(gsbResult.getPointer())) { + PrintOptions opts; + opts.ProtocolQualifiedDependentMemberTypes = true; + + llvm::errs() << "RequirementMachine generic signature minimization is broken:\n"; + llvm::errs() << "RequirementMachine says: "; + rqmResult.getPointer()->print(llvm::errs(), opts); + llvm::errs() << "\n"; + llvm::errs() << "GenericSignatureBuilder says: "; + gsbResult.getPointer()->print(llvm::errs(), opts); + llvm::errs() << "\n"; + + if (ctx.LangOpts.RequirementMachineAbstractSignatures + == RequirementMachineMode::Verify) + abort(); + } + + return rqmResult; + } + } +} + /// Check whether the inputs to the \c AbstractGenericSignatureRequest are /// all canonical. static bool isCanonicalRequest(GenericSignature baseSignature, @@ -8246,7 +8319,7 @@ static bool isCanonicalRequest(GenericSignature baseSignature, } GenericSignatureWithError -AbstractGenericSignatureRequest::evaluate( +AbstractGenericSignatureRequestGSB::evaluate( Evaluator &evaluator, const GenericSignatureImpl *baseSignatureImpl, SmallVector addedParameters, @@ -8295,7 +8368,7 @@ AbstractGenericSignatureRequest::evaluate( // Build the canonical signature. auto canSignatureResult = evaluateOrDefault( ctx.evaluator, - AbstractGenericSignatureRequest{ + AbstractGenericSignatureRequestGSB{ canBaseSignature.getPointer(), std::move(canAddedParameters), std::move(canAddedRequirements)}, GenericSignatureWithError()); @@ -8336,38 +8409,68 @@ AbstractGenericSignatureRequest::evaluate( canSignatureResult.getInt()); } - auto buildViaGSB = [&]() { - // Create a generic signature that will form the signature. - GenericSignatureBuilder builder(ctx); - if (baseSignature) - builder.addGenericSignature(baseSignature); + // Create a generic signature that will form the signature. + GenericSignatureBuilder builder(ctx); + if (baseSignature) + builder.addGenericSignature(baseSignature); - auto source = - GenericSignatureBuilder::FloatingRequirementSource::forAbstract(); + auto source = + GenericSignatureBuilder::FloatingRequirementSource::forAbstract(); + + for (auto param : addedParameters) + builder.addGenericParameter(param); + + for (const auto &req : addedRequirements) + builder.addRequirement(req, source, nullptr); - for (auto param : addedParameters) - builder.addGenericParameter(param); + bool hadError = builder.hadAnyError(); + auto result = std::move(builder).computeGenericSignature( + /*allowConcreteGenericParams=*/true); + return GenericSignatureWithError(result, hadError); +} - for (const auto &req : addedRequirements) - builder.addRequirement(req, source, nullptr); +GenericSignatureWithError +InferredGenericSignatureRequest::evaluate( + Evaluator &evaluator, + ModuleDecl *parentModule, + const GenericSignatureImpl *parentSig, + GenericParamList *genericParams, + WhereClauseOwner whereClause, + SmallVector addedRequirements, + SmallVector inferenceSources, + bool allowConcreteGenericParams) const { - bool hadError = builder.hadAnyError(); - auto result = std::move(builder).computeGenericSignature( - /*allowConcreteGenericParams=*/true); - return GenericSignatureWithError(result, hadError); + auto &ctx = parentModule->getASTContext(); + + auto buildViaGSB = [&]() { + return evaluateOrDefault( + ctx.evaluator, + InferredGenericSignatureRequestGSB{ + parentModule, + parentSig, + genericParams, + whereClause, + addedRequirements, + inferenceSources, + allowConcreteGenericParams}, + GenericSignatureWithError()); }; auto buildViaRQM = [&]() { return evaluateOrDefault( ctx.evaluator, - AbstractGenericSignatureRequestRQM{ - baseSignature.getPointer(), - std::move(addedParameters), - std::move(addedRequirements)}, + InferredGenericSignatureRequestRQM{ + parentModule, + parentSig, + genericParams, + whereClause, + addedRequirements, + inferenceSources, + allowConcreteGenericParams}, GenericSignatureWithError()); }; - switch (ctx.LangOpts.RequirementMachineAbstractSignatures) { + switch (ctx.LangOpts.RequirementMachineInferredSignatures) { case RequirementMachineMode::Disabled: return buildViaGSB(); @@ -8394,7 +8497,7 @@ AbstractGenericSignatureRequest::evaluate( gsbResult.getPointer()->print(llvm::errs(), opts); llvm::errs() << "\n"; - if (ctx.LangOpts.RequirementMachineAbstractSignatures + if (ctx.LangOpts.RequirementMachineInferredSignatures == RequirementMachineMode::Verify) abort(); } @@ -8405,7 +8508,7 @@ AbstractGenericSignatureRequest::evaluate( } GenericSignatureWithError -InferredGenericSignatureRequest::evaluate( +InferredGenericSignatureRequestGSB::evaluate( Evaluator &evaluator, ModuleDecl *parentModule, const GenericSignatureImpl *parentSig, @@ -8414,174 +8517,120 @@ InferredGenericSignatureRequest::evaluate( SmallVector addedRequirements, SmallVector inferenceSources, bool allowConcreteGenericParams) const { - auto buildViaGSB = [&]() { - GenericSignatureBuilder builder(parentModule->getASTContext()); - - // If there is a parent context, add the generic parameters and requirements - // from that context. - builder.addGenericSignature(parentSig); - - DeclContext *lookupDC = nullptr; - - const auto visitRequirement = [&](const Requirement &req, - RequirementRepr *reqRepr) { - const auto source = FloatingRequirementSource::forExplicit( - reqRepr->getSeparatorLoc()); - - // If we're extending a protocol and adding a redundant requirement, - // for example, `extension Foo where Self: Foo`, then emit a - // diagnostic. - - if (auto decl = lookupDC->getAsDecl()) { - if (auto extDecl = dyn_cast(decl)) { - auto extType = extDecl->getDeclaredInterfaceType(); - auto extSelfType = extDecl->getSelfInterfaceType(); - auto reqLHSType = req.getFirstType(); - auto reqRHSType = req.getSecondType(); - - if (extType->isExistentialType() && - reqLHSType->isEqual(extSelfType) && - reqRHSType->isEqual(extType)) { - - auto &ctx = extDecl->getASTContext(); - ctx.Diags.diagnose(extDecl->getLoc(), - diag::protocol_extension_redundant_requirement, - extType->getString(), - extSelfType->getString(), - reqRHSType->getString()); - } + GenericSignatureBuilder builder(parentModule->getASTContext()); + + // If there is a parent context, add the generic parameters and requirements + // from that context. + builder.addGenericSignature(parentSig); + + DeclContext *lookupDC = nullptr; + + const auto visitRequirement = [&](const Requirement &req, + RequirementRepr *reqRepr) { + const auto source = FloatingRequirementSource::forExplicit( + reqRepr->getSeparatorLoc()); + + // If we're extending a protocol and adding a redundant requirement, + // for example, `extension Foo where Self: Foo`, then emit a + // diagnostic. + + if (auto decl = lookupDC->getAsDecl()) { + if (auto extDecl = dyn_cast(decl)) { + auto extType = extDecl->getDeclaredInterfaceType(); + auto extSelfType = extDecl->getSelfInterfaceType(); + auto reqLHSType = req.getFirstType(); + auto reqRHSType = req.getSecondType(); + + if (extType->isExistentialType() && + reqLHSType->isEqual(extSelfType) && + reqRHSType->isEqual(extType)) { + + auto &ctx = extDecl->getASTContext(); + ctx.Diags.diagnose(extDecl->getLoc(), + diag::protocol_extension_redundant_requirement, + extType->getString(), + extSelfType->getString(), + reqRHSType->getString()); } } - - builder.addRequirement(req, reqRepr, source, - lookupDC->getParentModule()); - return false; - }; - - if (genericParams) { - // Extensions never have a parent signature. - if (genericParams->getOuterParameters()) - assert(parentSig == nullptr); - - // Type check the generic parameters, treating all generic type - // parameters as dependent, unresolved. - SmallVector gpLists; - for (auto *outerParams = genericParams; - outerParams != nullptr; - outerParams = outerParams->getOuterParameters()) { - gpLists.push_back(outerParams); - } - - // The generic parameter lists MUST appear from innermost to outermost. - // We walk them backwards to order outer requirements before - // inner requirements. - for (auto &genericParams : llvm::reverse(gpLists)) { - assert(genericParams->size() > 0 && - "Parsed an empty generic parameter list?"); - - // First, add the generic parameters to the generic signature builder. - // Do this before checking the inheritance clause, since it may - // itself be dependent on one of these parameters. - for (const auto param : *genericParams) - builder.addGenericParameter(param); - - // Add the requirements for each of the generic parameters to the builder. - // Now, check the inheritance clauses of each parameter. - for (const auto param : *genericParams) - builder.addGenericParameterRequirements(param); - - // Determine where and how to perform name lookup. - lookupDC = genericParams->begin()[0]->getDeclContext(); - - // Add the requirements clause to the builder. - WhereClauseOwner(lookupDC, genericParams) - .visitRequirements(TypeResolutionStage::Structural, - visitRequirement); - } - } - - if (whereClause) { - lookupDC = whereClause.dc; - std::move(whereClause).visitRequirements( - TypeResolutionStage::Structural, visitRequirement); - } - - /// Perform any remaining requirement inference. - for (auto sourcePair : inferenceSources) { - auto *typeRepr = sourcePair.getTypeRepr(); - auto source = - FloatingRequirementSource::forInferred( - typeRepr ? typeRepr->getStartLoc() : SourceLoc()); - - builder.inferRequirements(*parentModule, - sourcePair.getType(), - source); } - - // Finish by adding any remaining requirements. - auto source = - FloatingRequirementSource::forInferred(SourceLoc()); - - for (const auto &req : addedRequirements) - builder.addRequirement(req, source, parentModule); - - bool hadError = builder.hadAnyError(); - auto result = std::move(builder).computeGenericSignature( - allowConcreteGenericParams); - return GenericSignatureWithError(result, hadError); - }; - - auto &ctx = parentModule->getASTContext(); - auto buildViaRQM = [&]() { - return evaluateOrDefault( - ctx.evaluator, - InferredGenericSignatureRequestRQM{ - parentModule, - parentSig, - genericParams, - whereClause, - addedRequirements, - inferenceSources, - allowConcreteGenericParams}, - GenericSignatureWithError()); + builder.addRequirement(req, reqRepr, source, + lookupDC->getParentModule()); + return false; }; - switch (ctx.LangOpts.RequirementMachineInferredSignatures) { - case RequirementMachineMode::Disabled: - return buildViaGSB(); + if (genericParams) { + // Extensions never have a parent signature. + if (genericParams->getOuterParameters()) + assert(parentSig == nullptr); - case RequirementMachineMode::Enabled: - return buildViaRQM(); + // Type check the generic parameters, treating all generic type + // parameters as dependent, unresolved. + SmallVector gpLists; + for (auto *outerParams = genericParams; + outerParams != nullptr; + outerParams = outerParams->getOuterParameters()) { + gpLists.push_back(outerParams); + } - case RequirementMachineMode::Verify: - case RequirementMachineMode::Check: { - auto rqmResult = buildViaRQM(); - auto gsbResult = buildViaGSB(); + // The generic parameter lists MUST appear from innermost to outermost. + // We walk them backwards to order outer requirements before + // inner requirements. + for (auto &genericParams : llvm::reverse(gpLists)) { + assert(genericParams->size() > 0 && + "Parsed an empty generic parameter list?"); - if (!rqmResult.getPointer() && !gsbResult.getPointer()) - return rqmResult; + // First, add the generic parameters to the generic signature builder. + // Do this before checking the inheritance clause, since it may + // itself be dependent on one of these parameters. + for (const auto param : *genericParams) + builder.addGenericParameter(param); - if (!rqmResult.getPointer()->isEqual(gsbResult.getPointer())) { - PrintOptions opts; - opts.ProtocolQualifiedDependentMemberTypes = true; + // Add the requirements for each of the generic parameters to the builder. + // Now, check the inheritance clauses of each parameter. + for (const auto param : *genericParams) + builder.addGenericParameterRequirements(param); - llvm::errs() << "RequirementMachine generic signature minimization is broken:\n"; - llvm::errs() << "RequirementMachine says: "; - rqmResult.getPointer()->print(llvm::errs(), opts); - llvm::errs() << "\n"; - llvm::errs() << "GenericSignatureBuilder says: "; - gsbResult.getPointer()->print(llvm::errs(), opts); - llvm::errs() << "\n"; + // Determine where and how to perform name lookup. + lookupDC = genericParams->begin()[0]->getDeclContext(); - if (ctx.LangOpts.RequirementMachineInferredSignatures - == RequirementMachineMode::Verify) - abort(); + // Add the requirements clause to the builder. + WhereClauseOwner(lookupDC, genericParams) + .visitRequirements(TypeResolutionStage::Structural, + visitRequirement); } + } - return rqmResult; + if (whereClause) { + lookupDC = whereClause.dc; + std::move(whereClause).visitRequirements( + TypeResolutionStage::Structural, visitRequirement); } + + /// Perform any remaining requirement inference. + for (auto sourcePair : inferenceSources) { + auto *typeRepr = sourcePair.getTypeRepr(); + auto source = + FloatingRequirementSource::forInferred( + typeRepr ? typeRepr->getStartLoc() : SourceLoc()); + + builder.inferRequirements(*parentModule, + sourcePair.getType(), + source); } + + // Finish by adding any remaining requirements. + auto source = + FloatingRequirementSource::forInferred(SourceLoc()); + + for (const auto &req : addedRequirements) + builder.addRequirement(req, source, parentModule); + + bool hadError = builder.hadAnyError(); + auto result = std::move(builder).computeGenericSignature( + allowConcreteGenericParams); + return GenericSignatureWithError(result, hadError); } RequirementSignature @@ -8589,50 +8638,11 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator, ProtocolDecl *proto) const { ASTContext &ctx = proto->getASTContext(); - // First check if we have a deserializable requirement signature. - if (proto->hasLazyRequirementSignature()) { - ++NumLazyRequirementSignaturesLoaded; - // FIXME: (transitional) increment the redundant "always-on" counter. - if (ctx.Stats) - ++ctx.Stats->getFrontendCounters().NumLazyRequirementSignaturesLoaded; - - auto contextData = static_cast( - ctx.getOrCreateLazyContextData(proto, nullptr)); - - SmallVector requirements; - SmallVector typeAliases; - contextData->loader->loadRequirementSignature( - proto, contextData->requirementSignatureData, - requirements, typeAliases); - return RequirementSignature(ctx.AllocateCopy(requirements), - ctx.AllocateCopy(typeAliases)); - } - auto buildViaGSB = [&]() { - GenericSignatureBuilder builder(proto->getASTContext(), - /*requirementSignature=*/true); - - // Add all of the generic parameters. - for (auto gp : *proto->getGenericParams()) - builder.addGenericParameter(gp); - - // Add the conformance of 'self' to the protocol. - auto selfType = - proto->getSelfInterfaceType()->castTo(); - auto requirement = - Requirement(RequirementKind::Conformance, selfType, - proto->getDeclaredInterfaceType()); - - builder.addRequirement( - requirement, - GenericSignatureBuilder::RequirementSource::forRequirementSignature( - builder, selfType, proto), - nullptr); - - auto reqSignature = std::move(builder).computeGenericSignature( - /*allowConcreteGenericParams=*/false, - /*requirementSignatureSelfProto=*/proto); - return RequirementSignature(reqSignature.getRequirements(), None); + return evaluateOrDefault( + ctx.evaluator, + RequirementSignatureRequestGSB{const_cast(proto)}, + RequirementSignature()); }; auto buildViaRQM = [&]() { @@ -8705,3 +8715,53 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator, } } } + +RequirementSignature +RequirementSignatureRequestGSB::evaluate(Evaluator &evaluator, + ProtocolDecl *proto) const { + ASTContext &ctx = proto->getASTContext(); + + // First check if we have a deserializable requirement signature. + if (proto->hasLazyRequirementSignature()) { + ++NumLazyRequirementSignaturesLoaded; + // FIXME: (transitional) increment the redundant "always-on" counter. + if (ctx.Stats) + ++ctx.Stats->getFrontendCounters().NumLazyRequirementSignaturesLoaded; + + auto contextData = static_cast( + ctx.getOrCreateLazyContextData(proto, nullptr)); + + SmallVector requirements; + SmallVector typeAliases; + contextData->loader->loadRequirementSignature( + proto, contextData->requirementSignatureData, + requirements, typeAliases); + return RequirementSignature(ctx.AllocateCopy(requirements), + ctx.AllocateCopy(typeAliases)); + } + + GenericSignatureBuilder builder(proto->getASTContext(), + /*requirementSignature=*/true); + + // Add all of the generic parameters. + for (auto gp : *proto->getGenericParams()) + builder.addGenericParameter(gp); + + // Add the conformance of 'self' to the protocol. + auto selfType = + proto->getSelfInterfaceType()->castTo(); + auto requirement = + Requirement(RequirementKind::Conformance, selfType, + proto->getDeclaredInterfaceType()); + + builder.addRequirement( + requirement, + GenericSignatureBuilder::RequirementSource::forRequirementSignature( + builder, selfType, proto), + nullptr); + + auto reqSignature = std::move(builder).computeGenericSignature( + /*allowConcreteGenericParams=*/false, + /*requirementSignatureSelfProto=*/proto); + return RequirementSignature(reqSignature.getRequirements(), None); +} diff --git a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp index 933438ab1814a..503a08ada83ba 100644 --- a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp +++ b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp @@ -88,8 +88,22 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator, ASTContext &ctx = proto->getASTContext(); // First check if we have a deserializable requirement signature. - assert(!proto->hasLazyRequirementSignature() && - "Should be handled in RequirementSignatureRequest"); + if (proto->hasLazyRequirementSignature()) { + // FIXME: (transitional) increment the redundant "always-on" counter. + if (ctx.Stats) + ++ctx.Stats->getFrontendCounters().NumLazyRequirementSignaturesLoaded; + + auto contextData = static_cast( + ctx.getOrCreateLazyContextData(proto, nullptr)); + + SmallVector requirements; + SmallVector typeAliases; + contextData->loader->loadRequirementSignature( + proto, contextData->requirementSignatureData, + requirements, typeAliases); + return RequirementSignature(ctx.AllocateCopy(requirements), + ctx.AllocateCopy(typeAliases)); + } // We build requirement signatures for all protocols in a strongly connected // component at the same time. @@ -207,6 +221,27 @@ RequirementMachine::computeMinimalGenericSignatureRequirements( return reqs; } +/// Check whether the inputs to the \c AbstractGenericSignatureRequest are +/// all canonical. +static bool isCanonicalRequest(GenericSignature baseSignature, + ArrayRef genericParams, + ArrayRef requirements) { + if (baseSignature && !baseSignature->isCanonical()) + return false; + + for (auto gp : genericParams) { + if (!gp->isCanonical()) + return false; + } + + for (const auto &req : requirements) { + if (!req.isCanonical()) + return false; + } + + return true; +} + GenericSignatureWithError AbstractGenericSignatureRequestRQM::evaluate( Evaluator &evaluator, @@ -238,6 +273,69 @@ AbstractGenericSignatureRequestRQM::evaluate( return GenericSignatureWithError(result, /*hadError=*/false); } + // If the request is non-canonical, we won't need to build our own + // generic signature builder. + if (!isCanonicalRequest(baseSignature, addedParameters, addedRequirements)) { + // Canonicalize the inputs so we can form the canonical request. + auto canBaseSignature = baseSignature.getCanonicalSignature(); + + SmallVector canAddedParameters; + canAddedParameters.reserve(addedParameters.size()); + for (auto gp : addedParameters) { + auto canGP = gp->getCanonicalType()->castTo(); + canAddedParameters.push_back(canGP); + } + + SmallVector canAddedRequirements; + canAddedRequirements.reserve(addedRequirements.size()); + for (const auto &req : addedRequirements) { + canAddedRequirements.push_back(req.getCanonical()); + } + + // Build the canonical signature. + auto canSignatureResult = evaluateOrDefault( + ctx.evaluator, + AbstractGenericSignatureRequestRQM{ + canBaseSignature.getPointer(), std::move(canAddedParameters), + std::move(canAddedRequirements)}, + GenericSignatureWithError()); + if (!canSignatureResult.getPointer()) + return GenericSignatureWithError(); + + // Substitute in the original generic parameters to form the sugared + // result the original request wanted. + auto canSignature = canSignatureResult.getPointer(); + SmallVector resugaredParameters; + resugaredParameters.reserve(canSignature.getGenericParams().size()); + if (baseSignature) { + resugaredParameters.append(baseSignature.getGenericParams().begin(), + baseSignature.getGenericParams().end()); + } + resugaredParameters.append(addedParameters.begin(), addedParameters.end()); + assert(resugaredParameters.size() == + canSignature.getGenericParams().size()); + + SmallVector resugaredRequirements; + resugaredRequirements.reserve(canSignature.getRequirements().size()); + for (const auto &req : canSignature.getRequirements()) { + auto resugaredReq = req.subst( + [&](SubstitutableType *type) { + if (auto gp = dyn_cast(type)) { + unsigned ordinal = canSignature->getGenericParamOrdinal(gp); + return Type(resugaredParameters[ordinal]); + } + return Type(type); + }, + MakeAbstractConformanceForGenericType(), + SubstFlags::AllowLoweredTypes); + resugaredRequirements.push_back(*resugaredReq); + } + + return GenericSignatureWithError( + GenericSignature::get(resugaredParameters, resugaredRequirements), + canSignatureResult.getInt()); + } + SmallVector requirements; for (auto req : baseSignature.getRequirements()) requirements.push_back({req, SourceLoc(), /*wasInferred=*/false}); diff --git a/lib/AST/TypeCheckRequests.cpp b/lib/AST/TypeCheckRequests.cpp index 10ac962f6e67c..12d0857ef8a91 100644 --- a/lib/AST/TypeCheckRequests.cpp +++ b/lib/AST/TypeCheckRequests.cpp @@ -797,6 +797,14 @@ void InferredGenericSignatureRequest::noteCycleStep(DiagnosticEngine &d) const { // into this request. See rdar://55263708 } +void InferredGenericSignatureRequestGSB::noteCycleStep(DiagnosticEngine &d) const { + // For now, the GSB does a better job of describing the exact structure of + // the cycle. + // + // FIXME: We should consider merging the circularity handling the GSB does + // into this request. See rdar://55263708 +} + void InferredGenericSignatureRequestRQM::noteCycleStep(DiagnosticEngine &d) const { // For now, the GSB does a better job of describing the exact structure of // the cycle. From f5b3d1970384c58c606cd0202dc5f3d29b558987 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 13 Mar 2022 17:22:12 -0400 Subject: [PATCH 033/242] RequirementMachine: Replace hadError() with getErrors() returning an OptionSet This gives us more fine-grained information which will be plumbed through the various requests. --- include/swift/AST/GenericSignature.h | 23 +++++++++++++++ include/swift/AST/RequirementSignature.h | 14 +++++++-- include/swift/AST/TypeCheckRequests.h | 6 ---- lib/AST/GenericSignatureBuilder.cpp | 18 +++++++----- .../RequirementMachine/HomotopyReduction.cpp | 24 +++++++-------- .../RequirementMachine/RequirementMachine.cpp | 7 ++--- .../RequirementMachine/RequirementMachine.h | 2 +- .../RequirementMachineRequests.cpp | 29 ++++++++++--------- lib/AST/RequirementMachine/RewriteSystem.h | 3 +- 9 files changed, 78 insertions(+), 48 deletions(-) diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index 34930eb97f61f..9380e9f9296b9 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -529,6 +529,29 @@ GenericSignature buildGenericSignature( SmallVector addedParameters, SmallVector addedRequirements); +/// Summary of error conditions detected by the Requirement Machine. +enum class GenericSignatureErrorFlags { + /// The original requirements referenced a non-existent type parameter. + HasUnresolvedType = (1<<0), + + /// The original requirements were in conflict with each other, meaning + /// there are no possible concrete substitutions which statisfy the + /// generic signature. + HasConflict = (1<<1), + + /// The Knuth-Bendix completion procedure failed to construct a confluent + /// rewrite system. + CompletionFailed = (1<<2) +}; + +using GenericSignatureErrors = OptionSet; + +/// AbstractGenericSignatureRequest and InferredGenericSignatureRequest +/// return this type, which stores a GenericSignature together with the +/// above set of error flags. +using GenericSignatureWithError = llvm::PointerIntPair; + } // end namespace swift namespace llvm { diff --git a/include/swift/AST/RequirementSignature.h b/include/swift/AST/RequirementSignature.h index 1b185fdd1850f..9eef33bbe0fde 100644 --- a/include/swift/AST/RequirementSignature.h +++ b/include/swift/AST/RequirementSignature.h @@ -17,6 +17,7 @@ #ifndef SWIFT_AST_REQUIREMENT_SIGNATURE_H #define SWIFT_AST_REQUIREMENT_SIGNATURE_H +#include "swift/AST/GenericSignature.h" #include "swift/AST/Type.h" namespace swift { @@ -42,13 +43,16 @@ class ProtocolTypeAlias final { class RequirementSignature final { ArrayRef Requirements; ArrayRef TypeAliases; + GenericSignatureErrors Errors; public: - RequirementSignature() = default; + RequirementSignature(GenericSignatureErrors errors = GenericSignatureErrors()) + : Errors(errors) {} RequirementSignature(ArrayRef requirements, - ArrayRef typeAliases) - : Requirements(requirements), TypeAliases(typeAliases) {} + ArrayRef typeAliases, + GenericSignatureErrors errors = GenericSignatureErrors()) + : Requirements(requirements), TypeAliases(typeAliases), Errors(errors) {} /// The requirements including any inherited protocols and conformances for /// associated types that are introduced in this protocol. @@ -65,6 +69,10 @@ class RequirementSignature final { ArrayRef getTypeAliases() const { return TypeAliases; } + + GenericSignatureErrors getErrors() const { + return Errors; + } }; } // end namespace swift diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 644d4e1d228fa..eac22091a3006 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1747,12 +1747,6 @@ class ClassAncestryFlagsRequest : void simple_display(llvm::raw_ostream &out, AncestryFlags value); -/// AbstractGenericSignatureRequest and InferredGenericSignatureRequest -/// return this type, which stores a GenericSignature together with a bit -/// indicating if there were any errors detected in the original -/// requirements. -using GenericSignatureWithError = llvm::PointerIntPair; - class AbstractGenericSignatureRequest : public SimpleRequestgetASTContext() @@ -8328,7 +8328,7 @@ AbstractGenericSignatureRequestGSB::evaluate( // If nothing is added to the base signature, just return the base // signature. if (addedParameters.empty() && addedRequirements.empty()) - return GenericSignatureWithError(baseSignature, /*hadError=*/false); + return GenericSignatureWithError(baseSignature, GenericSignatureErrors()); ASTContext &ctx = addedParameters.empty() ? addedRequirements.front().getFirstType()->getASTContext() @@ -8343,7 +8343,7 @@ AbstractGenericSignatureRequestGSB::evaluate( auto result = GenericSignature::get(addedParameters, baseSignature.getRequirements()); - return GenericSignatureWithError(result, /*hadError=*/false); + return GenericSignatureWithError(result, GenericSignatureErrors()); } // If the request is non-canonical, we won't need to build our own @@ -8423,10 +8423,12 @@ AbstractGenericSignatureRequestGSB::evaluate( for (const auto &req : addedRequirements) builder.addRequirement(req, source, nullptr); - bool hadError = builder.hadAnyError(); + GenericSignatureErrors errorFlags; + if (builder.hadAnyError()) + errorFlags |= GenericSignatureErrorFlags::HasUnresolvedType; auto result = std::move(builder).computeGenericSignature( /*allowConcreteGenericParams=*/true); - return GenericSignatureWithError(result, hadError); + return GenericSignatureWithError(result, errorFlags); } GenericSignatureWithError @@ -8627,10 +8629,12 @@ InferredGenericSignatureRequestGSB::evaluate( for (const auto &req : addedRequirements) builder.addRequirement(req, source, parentModule); - bool hadError = builder.hadAnyError(); + GenericSignatureErrors errorFlags; + if (builder.hadAnyError()) + errorFlags |= GenericSignatureErrorFlags::HasUnresolvedType; auto result = std::move(builder).computeGenericSignature( allowConcreteGenericParams); - return GenericSignatureWithError(result, hadError); + return GenericSignatureWithError(result, errorFlags); } RequirementSignature diff --git a/lib/AST/RequirementMachine/HomotopyReduction.cpp b/lib/AST/RequirementMachine/HomotopyReduction.cpp index 190d24198a290..3d15cfaaee861 100644 --- a/lib/AST/RequirementMachine/HomotopyReduction.cpp +++ b/lib/AST/RequirementMachine/HomotopyReduction.cpp @@ -930,31 +930,31 @@ void RewriteSystem::minimizeRewriteSystem() { normalizeRedundantRules(); } -/// In a conformance-valid rewrite system, any rule with unresolved symbols on -/// the left or right hand side should be redundant. The presence of unresolved -/// non-redundant rules means one of the original requirements written by the -/// user was invalid. -bool RewriteSystem::hadError() const { +/// Returns flags indicating if the rewrite system has unresolved or +/// conflicting rules in our minimization domain. +GenericSignatureErrors RewriteSystem::getErrors() const { assert(Complete); assert(Minimized); - for (const auto &rule : Rules) { - if (!isInMinimizationDomain(rule.getLHS().getRootProtocol())) - continue; + GenericSignatureErrors result; + for (const auto &rule : Rules) { if (rule.isPermanent()) continue; - if (rule.isConflicting()) - return true; + if (!isInMinimizationDomain(rule.getLHS().getRootProtocol())) + continue; if (!rule.isRedundant() && !rule.isProtocolTypeAliasRule() && rule.containsUnresolvedSymbols()) - return true; + result |= GenericSignatureErrorFlags::HasUnresolvedType; + + if (rule.isConflicting()) + result |= GenericSignatureErrorFlags::HasConflict; } - return false; + return result; } /// Collect all non-permanent, non-redundant rules whose domain is equal to diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp index c965e2efe86ff..c6aa61fcf9a66 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.cpp +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -294,10 +294,9 @@ bool RequirementMachine::isComplete() const { return Complete; } -bool RequirementMachine::hadError() const { - // FIXME: Implement other checks here - // FIXME: Assert if hadError() is true but we didn't emit any diagnostics? - return System.hadError(); +GenericSignatureErrors RequirementMachine::getErrors() const { + // FIXME: Assert if we had errors but we didn't emit any diagnostics? + return System.getErrors(); } void RequirementMachine::dump(llvm::raw_ostream &out) const { diff --git a/lib/AST/RequirementMachine/RequirementMachine.h b/lib/AST/RequirementMachine/RequirementMachine.h index 743e54f1ede1d..30883259c3c09 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.h +++ b/lib/AST/RequirementMachine/RequirementMachine.h @@ -155,7 +155,7 @@ class RequirementMachine final { std::string getRuleAsStringForDiagnostics(unsigned ruleID) const; - bool hadError() const; + GenericSignatureErrors getErrors() const; void verify(const MutableTerm &term) const; void dump(llvm::raw_ostream &out) const; diff --git a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp index 503a08ada83ba..44c79741e327c 100644 --- a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp +++ b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp @@ -76,7 +76,8 @@ RequirementMachine::computeMinimalProtocolRequirements() { reqs, aliases); result[proto] = RequirementSignature(ctx.AllocateCopy(reqs), - ctx.AllocateCopy(aliases)); + ctx.AllocateCopy(aliases), + getErrors()); } return result; @@ -133,11 +134,11 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator, if (otherProto != proto) { ctx.evaluator.cacheOutput( RequirementSignatureRequestRQM{const_cast(otherProto)}, - RequirementSignature()); + RequirementSignature(GenericSignatureErrorFlags::CompletionFailed)); } } - return RequirementSignature(); + return RequirementSignature(GenericSignatureErrorFlags::CompletionFailed); } auto minimalRequirements = machine->computeMinimalProtocolRequirements(); @@ -252,7 +253,7 @@ AbstractGenericSignatureRequestRQM::evaluate( // If nothing is added to the base signature, just return the base // signature. if (addedParameters.empty() && addedRequirements.empty()) - return GenericSignatureWithError(baseSignature, /*hadError=*/false); + return GenericSignatureWithError(baseSignature, GenericSignatureErrors()); ASTContext &ctx = addedParameters.empty() ? addedRequirements.front().getFirstType()->getASTContext() @@ -270,7 +271,7 @@ AbstractGenericSignatureRequestRQM::evaluate( if (addedRequirements.empty()) { auto result = GenericSignature::get(genericParams, baseSignature.getRequirements()); - return GenericSignatureWithError(result, /*hadError=*/false); + return GenericSignatureWithError(result, GenericSignatureErrors()); } // If the request is non-canonical, we won't need to build our own @@ -388,12 +389,12 @@ AbstractGenericSignatureRequestRQM::evaluate( /*reconstituteSugar=*/false); auto result = GenericSignature::get(genericParams, minimalRequirements); - bool hadError = machine->hadError(); + auto errorFlags = machine->getErrors(); - if (!hadError) + if (!errorFlags) result.verify(); - return GenericSignatureWithError(result, hadError); + return GenericSignatureWithError(result, errorFlags); } GenericSignatureWithError @@ -522,7 +523,8 @@ InferredGenericSignatureRequestRQM::evaluate( rule); auto result = GenericSignature::get(genericParams, {}); - return GenericSignatureWithError(result, /*hadError=*/true); + return GenericSignatureWithError( + result, GenericSignatureErrorFlags::CompletionFailed); } auto minimalRequirements = @@ -530,20 +532,19 @@ InferredGenericSignatureRequestRQM::evaluate( /*reconstituteSugar=*/true); auto result = GenericSignature::get(genericParams, minimalRequirements); - bool hadError = machine->hadError(); + auto errorFlags = machine->getErrors(); if (ctx.LangOpts.RequirementMachineInferredSignatures == RequirementMachineMode::Enabled) { machine->System.computeRedundantRequirementDiagnostics(errors); - hadError |= diagnoseRequirementErrors(ctx, errors, - allowConcreteGenericParams); + diagnoseRequirementErrors(ctx, errors, allowConcreteGenericParams); } // FIXME: Handle allowConcreteGenericParams // Check invariants. - if (!hadError) + if (!errorFlags) result.verify(); - return GenericSignatureWithError(result, hadError); + return GenericSignatureWithError(result, errorFlags); } diff --git a/lib/AST/RequirementMachine/RewriteSystem.h b/lib/AST/RequirementMachine/RewriteSystem.h index bdfe21374ed12..07d0822706e8e 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.h +++ b/lib/AST/RequirementMachine/RewriteSystem.h @@ -14,6 +14,7 @@ #define SWIFT_REWRITESYSTEM_H #include "swift/AST/Requirement.h" +#include "swift/AST/TypeCheckRequests.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/PointerUnion.h" @@ -537,7 +538,7 @@ class RewriteSystem final { void minimizeRewriteSystem(); - bool hadError() const; + GenericSignatureErrors getErrors() const; struct MinimizedProtocolRules { std::vector Requirements; From 6d2ebb3be91fa8b009d7c379c7db5e4d3a11feb2 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 13 Mar 2022 19:18:56 -0400 Subject: [PATCH 034/242] RequirementMachine: Allow mismatch in verify mode if there is a conflict Having the Requirement Machine produce the same output as the GenericSignatureBuilder when there are conflicting requirements is difficult. It is also pointless, since invalid code does not have a stable ABI. Instead, just skip verification if the Requirement Machine reported that there were conflicting rules in the current minimization domain. --- lib/AST/GenericSignatureBuilder.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 7e0a91081097a..e44261d3bc345 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -8275,7 +8275,8 @@ AbstractGenericSignatureRequest::evaluate( if (!rqmResult.getPointer() && !gsbResult.getPointer()) return rqmResult; - if (!rqmResult.getPointer()->isEqual(gsbResult.getPointer())) { + if (!rqmResult.getInt().contains(GenericSignatureErrorFlags::HasConflict) && + !rqmResult.getPointer()->isEqual(gsbResult.getPointer())) { PrintOptions opts; opts.ProtocolQualifiedDependentMemberTypes = true; @@ -8487,7 +8488,8 @@ InferredGenericSignatureRequest::evaluate( if (!rqmResult.getPointer() && !gsbResult.getPointer()) return rqmResult; - if (!rqmResult.getPointer()->isEqual(gsbResult.getPointer())) { + if (!rqmResult.getInt().contains(GenericSignatureErrorFlags::HasConflict) && + !rqmResult.getPointer()->isEqual(gsbResult.getPointer())) { PrintOptions opts; opts.ProtocolQualifiedDependentMemberTypes = true; @@ -8689,7 +8691,9 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator, auto rqmResult = buildViaRQM(); auto gsbResult = buildViaGSB(); - if (!compare(rqmResult.getRequirements(), gsbResult.getRequirements())) { + if (!rqmResult.getErrors().contains(GenericSignatureErrorFlags::HasConflict) && + !compare(rqmResult.getRequirements(), + gsbResult.getRequirements())) { PrintOptions opts; opts.ProtocolQualifiedDependentMemberTypes = true; From a8a281d637252175d094836a9a336a985dda5628 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 11 Mar 2022 23:28:55 -0500 Subject: [PATCH 035/242] RequirementMachine: Use recordConflict() for superclass conflicts Instead of calling markConflicting() directly, use the existing recordConflict() utility. --- .../PropertyUnification.cpp | 6 ++--- .../superclass_and_concrete_requirement.swift | 23 ++++++++++++++++++- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/AST/RequirementMachine/PropertyUnification.cpp b/lib/AST/RequirementMachine/PropertyUnification.cpp index db37394315f77..30177967f2274 100644 --- a/lib/AST/RequirementMachine/PropertyUnification.cpp +++ b/lib/AST/RequirementMachine/PropertyUnification.cpp @@ -228,7 +228,6 @@ void PropertyMap::recordSuperclassRelation(Term key, void PropertyMap::addSuperclassProperty( Term key, Symbol property, unsigned ruleID) { auto *props = getOrCreateProperties(key); - auto &newRule = System.getRule(ruleID); bool debug = Debug.contains(DebugFlags::ConcreteUnification); const auto *superclassDecl = property.getConcreteType() @@ -345,8 +344,9 @@ void PropertyMap::addSuperclassProperty( << props->SuperclassDecl->getName() << "\n"; } - // FIXME: Record the conflict better - newRule.markConflicting(); + auto &req = props->Superclasses[props->SuperclassDecl]; + for (const auto &pair : req.SuperclassRules) + System.recordConflict(pair.second, ruleID); } } diff --git a/test/Generics/superclass_and_concrete_requirement.swift b/test/Generics/superclass_and_concrete_requirement.swift index e7fcae88c8353..b7826064274ac 100644 --- a/test/Generics/superclass_and_concrete_requirement.swift +++ b/test/Generics/superclass_and_concrete_requirement.swift @@ -1,4 +1,5 @@ -// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=verify +// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=verify -requirement-machine-inferred-signatures=verify 2>&1 | %FileCheck %s class C {} @@ -8,18 +9,26 @@ struct S {} // CHECK-NEXT: Requirement signature: protocol P1 { associatedtype T where T : C, T == C + // expected-warning@-1 {{redundant superclass constraint 'Self.T' : 'C'}} + // expected-note@-2 {{superclass constraint 'Self.T' : 'C' implied here}} } // CHECK-LABEL: .P2@ // CHECK-NEXT: Requirement signature: protocol P2 { associatedtype T where T : C, T == S + // expected-error@-1 {{'Self.T' requires that 'S' inherit from 'C'}} + // expected-note@-2 {{superclass constraint 'Self.T' : 'C' implied here}} + // expected-note@-3 {{same-type constraint 'Self.T' == 'S' implied here}} } // CHECK-LABEL: .P3@ // CHECK-NEXT: Requirement signature: protocol P3 { associatedtype T where T == S, T : C + // expected-error@-1 {{'Self.T' requires that 'S' inherit from 'C'}} + // expected-note@-2 {{same-type constraint 'Self.T' == 'S' implied here}} + // expected-note@-3 {{superclass constraint 'Self.T' : 'C' implied here}} } protocol P4a { @@ -30,4 +39,16 @@ protocol P4a { // CHECK-NEXT: Requirement signature: protocol P4 { associatedtype T : P4 where T.T == S + // expected-error@-1 2{{same-type constraint type 'S' does not conform to required protocol 'P4'}} +} + +class D {} + +// CHECK-LABEL: .P5@ +// CHECK-NEXT: Requirement signature: +protocol P5 { + associatedtype T where T : C, T == D + // expected-error@-1 {{'Self.T' requires that 'D' inherit from 'C'}} + // expected-note@-2 {{superclass constraint 'Self.T' : 'C' implied here}} + // expected-note@-3 {{same-type constraint 'Self.T' == 'D' implied here}} } From 13c6611ee8682cc4c8ae7fd0b70e67d53c6ca99d Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 13 Mar 2022 19:04:20 -0400 Subject: [PATCH 036/242] RequirementMachine: Use recordConflict() in concretizeNestedTypesFromConcreteParent() Instead of calling markConflicting() directly, use the existing recordConflict() utility. --- lib/AST/RequirementMachine/ConcreteTypeWitness.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp index c8cd8f80f010b..2edb6a0f88547 100644 --- a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp +++ b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp @@ -152,17 +152,8 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent( // There is no relation between P and C here. // // With concrete types, a missing conformance is a conflict. - if (requirementKind == RequirementKind::SameType) { - // FIXME: Diagnose conflict - auto &concreteRule = System.getRule(concreteRuleID); - if (concreteRule.getRHS().size() == key.size()) - concreteRule.markConflicting(); - - auto &conformanceRule = System.getRule(conformanceRuleID); - if (!conformanceRule.isIdentityConformanceRule() && - conformanceRule.getRHS().size() == key.size()) - conformanceRule.markConflicting(); - } + if (requirementKind == RequirementKind::SameType) + System.recordConflict(conformanceRuleID, concreteRuleID); if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { llvm::dbgs() << "^^ " << concreteType << " does not conform to " From 7b01ff60f03b31759f1ccddb95dfa6aa9edaf9ac Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 13 Mar 2022 19:18:11 -0400 Subject: [PATCH 037/242] RequirementMachine: Simplify RewriteSystem::processConflicts() Remove the logic which incorrectly attempted to simulate the GenericSignatureBuilder's minimization behavior in the presence of conflicts, since it doesn't matter anymore. --- .../RequirementMachine/HomotopyReduction.cpp | 43 +++++++------------ test/Generics/concrete_conflict.swift | 2 +- .../superclass_and_concrete_requirement.swift | 4 +- test/attr/attr_specialize.swift | 2 + 4 files changed, 21 insertions(+), 30 deletions(-) diff --git a/lib/AST/RequirementMachine/HomotopyReduction.cpp b/lib/AST/RequirementMachine/HomotopyReduction.cpp index 3d15cfaaee861..d149e24f66c2d 100644 --- a/lib/AST/RequirementMachine/HomotopyReduction.cpp +++ b/lib/AST/RequirementMachine/HomotopyReduction.cpp @@ -244,36 +244,25 @@ void RewriteSystem::propagateRedundantRequirementIDs() { } } -/// After propagating the 'explicit' bit on rules, process pairs of -/// conflicting rules, marking one or both of the rules as conflicting, -/// which instructs minimization to drop them. +/// Process pairs of conflicting rules, marking the more specific rule as +/// conflicting, which instructs minimization to drop this rule. void RewriteSystem::processConflicts() { for (auto pair : ConflictingRules) { - auto existingRuleID = pair.first; - auto newRuleID = pair.second; - - auto *existingRule = &getRule(existingRuleID); - auto *newRule = &getRule(newRuleID); - - auto existingKind = existingRule->isPropertyRule()->getKind(); - auto newKind = newRule->isPropertyRule()->getKind(); - - // The GSB preferred to drop an explicit rule in a conflict, but - // only if the kinds were the same. - if (existingRule->isExplicit() && !newRule->isExplicit() && - existingKind == newKind) { - std::swap(existingRule, newRule); - } - - if (newRule->getRHS().size() >= existingRule->getRHS().size()) { + auto *existingRule = &getRule(pair.first); + auto *newRule = &getRule(pair.second); + + // The identity conformance rule ([P].[P] => [P]) will conflict with + // a concrete type requirement in an invalid protocol declaration + // where 'Self' is constrained to a type that does not conform to + // the protocol. This rule is permanent, so don't mark it as + // conflicting in this case. + + if (!existingRule->isIdentityConformanceRule() && + existingRule->getRHS().size() >= newRule->getRHS().size()) + existingRule->markConflicting(); + if (!newRule->isIdentityConformanceRule() && + newRule->getRHS().size() >= existingRule->getRHS().size()) newRule->markConflicting(); - } else if (existingKind != Symbol::Kind::Superclass && - existingKind == newKind) { - // The GSB only dropped the new rule in the case of a conflicting - // superclass requirement, so maintain that behavior here. - if (existingRule->getRHS().size() >= newRule->getRHS().size()) - existingRule->markConflicting(); - } // FIXME: Diagnose the conflict later. } diff --git a/test/Generics/concrete_conflict.swift b/test/Generics/concrete_conflict.swift index 6b4295c2c3e02..a440bbab68cec 100644 --- a/test/Generics/concrete_conflict.swift +++ b/test/Generics/concrete_conflict.swift @@ -36,7 +36,7 @@ class Class {} extension Class where T == Bool { // CHECK-LABEL: .badRequirement()@ - // CHECK-NEXT: + // CHECK-NEXT: func badRequirement() where T == Int { } // expected-error@-1 {{generic parameter 'T' cannot be equal to both 'Int' and 'Bool'}} } \ No newline at end of file diff --git a/test/Generics/superclass_and_concrete_requirement.swift b/test/Generics/superclass_and_concrete_requirement.swift index b7826064274ac..a837722540f21 100644 --- a/test/Generics/superclass_and_concrete_requirement.swift +++ b/test/Generics/superclass_and_concrete_requirement.swift @@ -14,7 +14,7 @@ protocol P1 { } // CHECK-LABEL: .P2@ -// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Requirement signature: protocol P2 { associatedtype T where T : C, T == S // expected-error@-1 {{'Self.T' requires that 'S' inherit from 'C'}} @@ -23,7 +23,7 @@ protocol P2 { } // CHECK-LABEL: .P3@ -// CHECK-NEXT: Requirement signature: +// CHECK-NEXT: Requirement signature: protocol P3 { associatedtype T where T == S, T : C // expected-error@-1 {{'Self.T' requires that 'S' inherit from 'C'}} diff --git a/test/attr/attr_specialize.swift b/test/attr/attr_specialize.swift index e22a4dcff024d..8a19db851bfed 100644 --- a/test/attr/attr_specialize.swift +++ b/test/attr/attr_specialize.swift @@ -177,6 +177,8 @@ func funcWithForbiddenSpecializeRequirement(_ t: T) { // expected-warning@-3{{redundant constraint 'T' : '_Trivial'}} // expected-note@-4 {{constraint 'T' : '_Trivial' implied here}} // expected-note@-5 2{{constraint conflicts with 'T' : '_Trivial(32)'}} +// expected-error@-6 {{too few generic parameters are specified in '_specialize' attribute (got 0, but expected 1)}} +// expected-note@-7 {{missing constraint for 'T' in '_specialize' attribute}} @_specialize(where T: _Trivial, T: _Trivial(64)) // expected-warning@-1{{redundant constraint 'T' : '_Trivial'}} // expected-note@-2 1{{constraint 'T' : '_Trivial' implied here}} From 226d2c89a05f1308ecc059fcd702b6c5cc5f5a89 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 13 Mar 2022 22:53:16 -0400 Subject: [PATCH 038/242] RequirementMachine: Disable verification in a few highly-invalid compiler_crashers where it's not useful --- .../00228-swift-clangimporter-loadextensions.swift | 2 +- .../00237-swift-declrefexpr-setspecialized.swift | 2 +- .../compiler_crashers_fixed/00365-getselftypeforcontainer.swift | 2 +- .../01107-std-function-func-swift-type-subst.swift | 2 +- .../01177-std-function-func-swift-type-subst.swift | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/validation-test/compiler_crashers_fixed/00228-swift-clangimporter-loadextensions.swift b/validation-test/compiler_crashers_fixed/00228-swift-clangimporter-loadextensions.swift index 80b7c390cef29..7640644f4b16f 100644 --- a/validation-test/compiler_crashers_fixed/00228-swift-clangimporter-loadextensions.swift +++ b/validation-test/compiler_crashers_fixed/00228-swift-clangimporter-loadextensions.swift @@ -5,7 +5,7 @@ // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// RUN: not %target-swift-frontend %s -typecheck +// RUN: not %target-swift-frontend %s -typecheck -requirement-machine-inferred-signatures=on func s() -> o { r q { } diff --git a/validation-test/compiler_crashers_fixed/00237-swift-declrefexpr-setspecialized.swift b/validation-test/compiler_crashers_fixed/00237-swift-declrefexpr-setspecialized.swift index 9d7ed5dae40a9..2cf979e87b21d 100644 --- a/validation-test/compiler_crashers_fixed/00237-swift-declrefexpr-setspecialized.swift +++ b/validation-test/compiler_crashers_fixed/00237-swift-declrefexpr-setspecialized.swift @@ -5,7 +5,7 @@ // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// RUN: not %target-swift-frontend %s -typecheck +// RUN: not %target-swift-frontend %s -typecheck -requirement-machine-inferred-signatures=on func b(c) -> (() -> d) { } struct d { diff --git a/validation-test/compiler_crashers_fixed/00365-getselftypeforcontainer.swift b/validation-test/compiler_crashers_fixed/00365-getselftypeforcontainer.swift index 81415830c3d66..36295df56b549 100644 --- a/validation-test/compiler_crashers_fixed/00365-getselftypeforcontainer.swift +++ b/validation-test/compiler_crashers_fixed/00365-getselftypeforcontainer.swift @@ -5,7 +5,7 @@ // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// RUN: not %target-swift-frontend %s -typecheck +// RUN: not %target-swift-frontend %s -typecheck -requirement-machine-inferred-signatures=on class d : e { } class d { diff --git a/validation-test/compiler_crashers_fixed/01107-std-function-func-swift-type-subst.swift b/validation-test/compiler_crashers_fixed/01107-std-function-func-swift-type-subst.swift index 413d0a8e433b4..6becbc00f599a 100644 --- a/validation-test/compiler_crashers_fixed/01107-std-function-func-swift-type-subst.swift +++ b/validation-test/compiler_crashers_fixed/01107-std-function-func-swift-type-subst.swift @@ -5,7 +5,7 @@ // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// RUN: not %target-swift-frontend %s -typecheck +// RUN: not %target-swift-frontend %s -typecheck -requirement-machine-inferred-signatures=on func b : e { } diff --git a/validation-test/compiler_crashers_fixed/01177-std-function-func-swift-type-subst.swift b/validation-test/compiler_crashers_fixed/01177-std-function-func-swift-type-subst.swift index b9d554e3bb785..5f6622f3c7686 100644 --- a/validation-test/compiler_crashers_fixed/01177-std-function-func-swift-type-subst.swift +++ b/validation-test/compiler_crashers_fixed/01177-std-function-func-swift-type-subst.swift @@ -5,7 +5,7 @@ // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// RUN: not %target-swift-frontend %s -typecheck +// RUN: not %target-swift-frontend %s -typecheck -requirement-machine-inferred-signatures=on class d : e { } class d { From 6a74ad7065ca1f45a0860099fcb4c55d13a894c8 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 13 Mar 2022 22:53:32 -0400 Subject: [PATCH 039/242] RequirementMachine: Drop GSB compatibility hack involving ErrorTypes Another hack we can remove now that 'verify' mode skips checking if there was a conflict. --- lib/AST/RequirementMachine/RequirementLowering.cpp | 3 --- .../compiler_crashers_2_fixed/0159-rdar40009245.swift | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 459a21a10c30d..0a5cd9b8a130b 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -105,9 +105,6 @@ static void desugarSameTypeRequirement(Type lhs, Type rhs, SourceLoc loc, } } matcher(loc, result, errors); - if (lhs->hasError() || rhs->hasError()) - return; - (void) matcher.match(lhs, rhs); // If neither side is directly a type parameter, the type parameter diff --git a/validation-test/compiler_crashers_2_fixed/0159-rdar40009245.swift b/validation-test/compiler_crashers_2_fixed/0159-rdar40009245.swift index 442d7928c0d1a..9becb7b44a2a1 100644 --- a/validation-test/compiler_crashers_2_fixed/0159-rdar40009245.swift +++ b/validation-test/compiler_crashers_2_fixed/0159-rdar40009245.swift @@ -2,6 +2,7 @@ protocol P { associatedtype A : P where A.X == Self + // expected-error@-1{{'X' is not a member type of type 'Self.A'}} associatedtype X : P where P.A == Self // expected-error@-1{{associated type 'A' can only be used with a concrete type or generic parameter base}} } From 305a1e42b60a2fffdf1bd1173c3e899546def093 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sat, 12 Mar 2022 00:20:59 -0500 Subject: [PATCH 040/242] RequirementMachine: Update some tests to pass with -requirement-machine-inferred-signatures=verify --- test/Constraints/same_types.swift | 72 ++++++++++++++++++- ...tocol_typealias_concrete_unification.swift | 15 +++- test/Generics/rdar62903491.swift | 47 ++++++------ test/Generics/rdar80503090.swift | 2 +- test/Generics/requirement_inference.swift | 11 ++- test/ModuleInterface/access-filter.swift | 6 +- .../specialized-extension.swift | 4 +- test/Serialization/Recovery/typedefs.swift | 4 +- test/attr/attr_specialize.swift | 8 ++- .../compiler_crashers_2_fixed/sr9584.swift | 6 +- 10 files changed, 131 insertions(+), 44 deletions(-) diff --git a/test/Constraints/same_types.swift b/test/Constraints/same_types.swift index e733223052172..566cec8ca49a5 100644 --- a/test/Constraints/same_types.swift +++ b/test/Constraints/same_types.swift @@ -1,4 +1,5 @@ -// RUN: %target-typecheck-verify-swift -verify-ignore-unknown +// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=off +// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s protocol Fooable { associatedtype Foo // expected-note{{protocol requires nested type 'Foo'; do you want to add it?}} @@ -38,16 +39,22 @@ struct NestedConstraint { } } +// CHECK-LABEL: same_types.(file).test2(_:u:)@ +// CHECK-NEXT: Generic signature: func test2(_ t: T, u: U) -> (X, X) where T.Foo == X, U.Foo == T.Foo { return (t.foo, u.foo) } +// CHECK-LABEL: same_types.(file).test2a(_:u:)@ +// CHECK-NEXT: Generic signature: func test2a(_ t: T, u: U) -> (X, X) where T.Foo == X, T.Foo == U.Foo { return (t.foo, u.foo) } +// CHECK-LABEL: same_types.(file).test3(_:u:)@ +// CHECK-NEXT: Generic signature: func test3(_ t: T, u: U) -> (X, X) where T.Foo == X, U.Foo == X, T.Foo == U.Foo { // expected-warning@-1{{redundant same-type constraint 'U.Foo' == 'X'}} @@ -55,6 +62,8 @@ func test3(_ t: T, u: U) -> (X, X) return (t.foo, u.foo) } +// CHECK-LABEL: same_types.(file).fail1(_:u:)@ +// CHECK-NEXT: Generic signature: func fail1< T: Fooable, U: Fooable >(_ t: T, u: U) -> (X, Y) @@ -63,6 +72,8 @@ func fail1< return (t.foo, u.foo) // expected-error{{cannot convert return expression of type '(X, X)' to return type '(X, Y)'}} } +// CHECK-LABEL: same_types.(file).fail2(_:u:)@ +// CHECK-NEXT: Generic signature: func fail2< T: Fooable, U: Fooable >(_ t: T, u: U) -> (X, Y) @@ -75,6 +86,8 @@ func test4(_ t: T) -> Y where T.Bar == Y { return t.bar } +// CHECK-LABEL: same_types.(file).fail3@ +// CHECK-NEXT: Generic signature: func fail3(_ t: T) -> X where T.Bar == X { // expected-error {{'X' does not conform to required protocol 'Fooable'}} return t.bar // expected-error{{cannot convert return expression of type 'T.Bar' }} @@ -88,11 +101,15 @@ func test6(_ t: T) -> (Y, X) where T.Bar == Y { return (t.bar, t.bar.foo) } +// CHECK-LABEL: same_types.(file).test7@ +// CHECK-NEXT: Generic signature: func test7(_ t: T) -> (Y, X) where T.Bar == Y, T.Bar.Foo == X { // expected-warning@-1{{neither type in same-type constraint ('Y.Foo' (aka 'X') or 'X') refers to a generic parameter or associated type}} return (t.bar, t.bar.foo) } +// CHECK-LABEL: same_types.(file).fail4@ +// CHECK-NEXT: Generic signature: func fail4(_ t: T) -> (Y, Z) where T.Bar == Y, @@ -100,6 +117,8 @@ func fail4(_ t: T) -> (Y, Z) return (t.bar, t.bar.foo) // expected-error{{cannot convert return expression of type '(Y, X)' to return type '(Y, Z)'}} } +// CHECK-LABEL: same_types.(file).fail5@ +// CHECK-NEXT: Generic signature: func fail5(_ t: T) -> (Y, Z) where T.Bar.Foo == Z, // expected-note{{same-type constraint 'T.Bar.Foo' == 'Z' written here}} @@ -107,6 +126,8 @@ func fail5(_ t: T) -> (Y, Z) return (t.bar, t.bar.foo) // expected-error{{cannot convert return expression of type '(Y, X)' to return type '(Y, Z)'}} } +// CHECK-LABEL: same_types.(file).test8@ +// CHECK-NEXT: Generic signature: func test8(_ t: T) where T.Foo == X, // expected-note{{same-type constraint 'T.Foo' == 'X' written here}} T.Foo == Y {} // expected-error{{'T.Foo' cannot be equal to both 'Y' and 'X'}} @@ -121,12 +142,16 @@ func fail6(_ t: T) -> Int where T == Int { // expected-error{{same-type requi return t } +// CHECK-LABEL: same_types.(file).test8(_:u:)@ +// CHECK-NEXT: Generic signature: func test8(_ t: T, u: U) -> (Y, Y, X, X) where T.Bar == Y, // expected-note{{same-type constraint 'U.Bar.Foo' == 'Y.Foo' (aka 'X') implied here}} U.Bar.Foo == X, T.Bar == U.Bar { // expected-warning{{redundant same-type constraint 'U.Bar.Foo' == 'X'}} return (t.bar, u.bar, t.bar.foo, u.bar.foo) } +// CHECK-LABEL: same_types.(file).test8a(_:u:)@ +// CHECK-NEXT: Generic signature: func test8a(_ t: T, u: U) -> (Y, Y, X, X) where T.Bar == Y, // expected-note{{same-type constraint 'U.Bar.Foo' == 'Y.Foo' (aka 'X') implied here}} @@ -134,6 +159,8 @@ func test8a(_ t: T, u: U) -> (Y, Y, X, X) return (t.bar, u.bar, t.bar.foo, u.bar.foo) } +// CHECK-LABEL: same_types.(file).test8b(_:u:)@ +// CHECK-NEXT: Generic signature: func test8b(_ t: T, u: U) where U.Bar.Foo == X, // expected-warning{{redundant same-type constraint 'U.Bar.Foo' == 'X'}} T.Bar == Y, // expected-note{{same-type constraint 'U.Bar.Foo' == 'Y.Foo' (aka 'X') implied here}} @@ -166,6 +193,8 @@ struct Q : P { } struct S1 { + // CHECK-LABEL: same_types.(file).S1.foo(x:y:)@ + // CHECK-NEXT: Generic signature: func foo(x: X, y: Y) where X == T.A, Y == T.B { print(X.self) print(Y.self) @@ -176,6 +205,8 @@ struct S1 { S1().foo(x: 1, y: 2) struct S2 where T.A == T.B { + // CHECK-LABEL: same_types.(file).S2.foo(x:y:)@ + // CHECK-NEXT: func foo(x: X, y: Y) where X == T.A, Y == T.B { // expected-error{{same-type requirement makes generic parameters 'X' and 'Y' equivalent}} print(X.self) print(Y.self) @@ -186,6 +217,8 @@ struct S2 where T.A == T.B { S2().foo(x: 1, y: 2) struct S3 { + // CHECK-LABEL: same_types.(file).S3.foo(x:y:)@ + // CHECK-NEXT: func foo(x: X, y: Y) where X == T.A, Y == T.A {} // expected-error{{same-type requirement makes generic parameters 'X' and 'Y' equivalent}} } S3().foo(x: 1, y: 2) @@ -208,6 +241,8 @@ struct QQ : P { } struct S4 { + // CHECK-LABEL: same_types.(file).S4.foo(x:)@ + // CHECK-NEXT: Generic signature: func foo(x: X) where X.A == T.A { print(x) print(X.self) @@ -223,6 +258,8 @@ protocol P1 { associatedtype Assoc } +// CHECK-LABEL: same_types.(file).structuralSameType1@ +// CHECK-NEXT: Generic signature: , B.[P1]Assoc == X1> func structuralSameType1(_: A, _: B, _: T, _: U, _: V, _: W) where A.Assoc == X1, B.Assoc == X1, A.Assoc == B.Assoc { } // expected-error@-1{{same-type requirement makes generic parameters 'T' and 'V' equivalent}} @@ -230,11 +267,15 @@ func structuralSameType1(_: A, _: B, _: T, _: U, _: V, typealias Tuple2 = (T, U) +// CHECK-LABEL: same_types.(file).structuralSameType2@ +// CHECK-NEXT: Generic signature: func structuralSameType2(_: A, _: B, _: T, _: U, _: V, _: W) where A.Assoc == Tuple2, B.Assoc == Tuple2, A.Assoc == B.Assoc { } // expected-error@-1{{same-type requirement makes generic parameters 'T' and 'V' equivalent}} // expected-error@-2{{same-type requirement makes generic parameters 'U' and 'W' equivalent}} +// CHECK-LABEL: same_types.(file).structuralSameType3@ +// CHECK-NEXT: Generic signature: func structuralSameType3(_: T, _: U, _: V, _: W) where X1 == X1 { } // expected-error@-1{{same-type requirement makes generic parameters 'T' and 'V' equivalent}} @@ -246,6 +287,8 @@ protocol P2 { associatedtype Assoc2 } +// CHECK-LABEL: same_types.(file).structuralSameTypeRecursive1@ +// CHECK-NEXT: Generic signature: func structuralSameTypeRecursive1(_: T, _: U) where T.Assoc1 == Tuple2 // expected-error{{same-type constraint 'T.Assoc1' == '(T.Assoc1, U)' is recursive}} { } @@ -257,6 +300,8 @@ protocol P4 { associatedtype A } +// CHECK-LABEL: same_types.(file).test9@ +// CHECK-NEXT: Generic signature: func test9(_: T) where T.A == X, T: P4, T.A: P3 { } // expected-error{{same-type constraint type 'X' does not conform to required protocol 'P3'}} // Same-type constraint conflict through protocol where clauses. @@ -273,6 +318,8 @@ struct X5a {} struct X5b { } +// CHECK-LABEL: same_types.(file).test9(_:u:)@ +// CHECK-NEXT: Generic signature: func test9(_ t: T, u: U) where T.Bar.Foo1 == X5a, // expected-note{{same-type constraint 'T.Bar.Foo1' == 'X5a' written here}} U.Bar.Foo2 == X5b, // expected-error{{'U.Bar.Foo2' cannot be equal to both 'X5b' and 'X5a'}} @@ -282,36 +329,57 @@ func test9(_ t: T, u: U) // FIXME: Remove -verify-ignore-unknown. // :0: error: unexpected error produced: generic parameter τ_0_0.Bar.Foo cannot be equal to both 'Y.Foo' (aka 'X') and 'Z' +// CHECK-LABEL: same_types.(file).testMetatypeSameType@ +// CHECK-NEXT: Generic signature: func testMetatypeSameType(_ t: T, _ u: U) where T.Type == U.Type { } // expected-error@-1{{same-type requirement makes generic parameters 'T' and 'U' equivalent}} // expected-warning@-2{{neither type in same-type constraint ('T.Type' or 'U.Type') refers to a generic parameter or associated type}} +// CHECK-LABEL: same_types.(file).testSameTypeCommutativity1@ +// CHECK-NEXT: Generic signature: func testSameTypeCommutativity1(_ t: T, _ u: U) where T.Type == U { } // Equivalent to U == T.Type // expected-error@-1{{same-type requirement makes generic parameter 'U' non-generic}} +// CHECK-LABEL: same_types.(file).testSameTypeCommutativity2@ +// CHECK-NEXT: Generic signature: func testSameTypeCommutativity2(_ t: T, _ u: U) where U? == T.Assoc { } // Ok, equivalent to T.Assoc == U? +// CHECK-LABEL: same_types.(file).testSameTypeCommutativity3@ +// CHECK-NEXT: Generic signature: ()> func testSameTypeCommutativity3(_ t: T, _ u: U) where (U) -> () == T.Assoc { } // Ok, equivalent to T.Assoc == (U) -> () +// CHECK-LABEL: same_types.(file).testSameTypeCommutativity4@ +// CHECK-NEXT: Generic signature: ()> func testSameTypeCommutativity4(_ t: T, _ u: U) where (U) -> () == T { } // Equivalent to T == (U) -> () // expected-error@-1{{same-type requirement makes generic parameter 'T' non-generic}} +// CHECK-LABEL: same_types.(file).testSameTypeCommutativity5@ +// CHECK-NEXT: Generic signature: func testSameTypeCommutativity5(_ t: T, _ u: U) where PPP & P3 == T.Assoc { } // Ok, equivalent to T.Assoc == PPP & P3 +// CHECK-LABEL: same_types.(file).testSameTypeCommutativity6@ +// CHECK-NEXT: Generic signature: func testSameTypeCommutativity6(_ t: T, _ u: U) where U & P3 == T.Assoc { } // Equivalent to T.Assoc == U & P3 // expected-error@-1 {{non-protocol, non-class type 'U' cannot be used within a protocol-constrained type}} -// rdar;//problem/46848889 +// rdar://problem/46848889 + +// CHECK-LABEL: same_types.(file).Foo@ +// CHECK-NEXT: Generic signature: struct Foo where A.Assoc == B.Assoc, A.Assoc == C.Assoc {} +// CHECK-LABEL: same_types.(file).Bar@ +// CHECK-NEXT: Generic signature: struct Bar where A.Assoc == B.Assoc { + // CHECK-LABEL: same_types.(file).Bar.f(with:)@ + // CHECK-NEXT: Generic signature: func f(with other: C) -> Foo where A.Assoc == C.Assoc { // expected-note@-1 {{previous same-type constraint 'B.Assoc' == 'C.Assoc' inferred from type here}} // expected-warning@-2 {{redundant same-type constraint 'A.Assoc' == 'C.Assoc'}} diff --git a/test/Generics/protocol_typealias_concrete_unification.swift b/test/Generics/protocol_typealias_concrete_unification.swift index 45ab8ba5d5673..148e1cf3ac9ae 100644 --- a/test/Generics/protocol_typealias_concrete_unification.swift +++ b/test/Generics/protocol_typealias_concrete_unification.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on +// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s protocol P1 { typealias T = Array @@ -9,4 +9,17 @@ protocol P2 { typealias T = Array } +// Note that the GenericSignatureBuilder did not record the 'T.[P1]U == Int' +// requirement. But I believe this is too aggressive. + +// CHECK-LABEL: .foo(_:u:)@ +// CHECK-NEXT: Generic signature: func foo(_: T, u: T.U) -> Int { return u } + +// CHECK-LABEL: .P3@ +// CHECK-NEXT: Requirement signature: +protocol P3 : P1, P2 {} + +// This conformance should succeed, and associated type inference should infer +// 'S.U == Int'. +struct S : P3 {} \ No newline at end of file diff --git a/test/Generics/rdar62903491.swift b/test/Generics/rdar62903491.swift index 19915a973f21f..1016142ffcb8a 100644 --- a/test/Generics/rdar62903491.swift +++ b/test/Generics/rdar62903491.swift @@ -1,5 +1,5 @@ -// RUN: %target-typecheck-verify-swift -// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=on +// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s protocol P { associatedtype X : P @@ -7,26 +7,22 @@ protocol P { // Anything that mentions 'T : P' and 'U : P' minimizes to 'U : P'. -// expected-warning@+2 {{redundant conformance constraint 'U' : 'P'}} -// expected-note@+1 {{conformance constraint 'U' : 'P' implied here}} +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P'}} func oneProtocol1(_: T, _: U) where T : P, U : P, T.X == U, U.X == T {} // CHECK-LABEL: oneProtocol1 // CHECK: Generic signature: -// expected-warning@+2 {{redundant conformance constraint 'U' : 'P'}} -// expected-note@+1 {{conformance constraint 'U' : 'P' implied here}} +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P'}} func oneProtocol2(_: T, _: U) where U : P, T : P, T.X == U, U.X == T {} // CHECK-LABEL: oneProtocol2 // CHECK: Generic signature: -// expected-warning@+2 {{redundant conformance constraint 'U' : 'P'}} -// expected-note@+1 {{conformance constraint 'U' : 'P' implied here}} +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P'}} func oneProtocol3(_: T, _: U) where T : P, T.X == U, U : P, U.X == T {} // CHECK-LABEL: oneProtocol3 // CHECK: Generic signature: -// expected-warning@+2 {{redundant conformance constraint 'U' : 'P'}} -// expected-note@+1 {{conformance constraint 'U' : 'P' implied here}} +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P'}} func oneProtocol4(_: T, _: U) where U : P, T.X == U, T : P, U.X == T {} // CHECK-LABEL: oneProtocol4 // CHECK: Generic signature: @@ -59,38 +55,43 @@ protocol P2 { associatedtype Y : P1 } -// expected-warning@+2 {{redundant conformance constraint 'U' : 'P2'}} -// expected-note@+1 {{conformance constraint 'U' : 'P2' implied here}} +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P2'}} func twoProtocols1(_: T, _: U) where T : P1, U : P2, T.X == U, U.Y == T {} // CHECK-LABEL: twoProtocols1 // CHECK: Generic signature: -// expected-warning@+2 {{redundant conformance constraint 'U' : 'P2'}} -// expected-note@+1 {{conformance constraint 'U' : 'P2' implied here}} +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P2'}} func twoProtocols2(_: T, _: U) where U : P2, T : P1, T.X == U, U.Y == T {} // CHECK-LABEL: twoProtocols2 // CHECK: Generic signature: -// expected-warning@+2 {{redundant conformance constraint 'U' : 'P2'}} -// expected-note@+1 {{conformance constraint 'U' : 'P2' implied here}} +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P2'}} func twoProtocols3(_: T, _: U) where T : P1, T.X == U, U : P2, U.Y == T {} // CHECK-LABEL: twoProtocols3 // CHECK: Generic signature: -// expected-warning@+2 {{redundant conformance constraint 'U' : 'P2'}} -// expected-note@+1 {{conformance constraint 'U' : 'P2' implied here}} +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P2'}} func twoProtocols4(_: T, _: U) where U : P2, T.X == U, T : P1, U.Y == T {} // CHECK-LABEL: twoProtocols4 // CHECK: Generic signature: -// expected-warning@+2 {{redundant conformance constraint 'U' : 'P2'}} -// expected-note@+1 {{conformance constraint 'U' : 'P2' implied here}} +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P2'}} func twoProtocols5(_: T, _: U) where T : P1, T.X == U, U.Y == T, U : P2 {} // CHECK-LABEL: twoProtocols5 // CHECK: Generic signature: -// expected-warning@+2 {{redundant conformance constraint 'T' : 'P1'}} -// expected-note@+1 {{conformance constraint 'T' : 'P1' implied here}} +// The GenericSignatureBuilder minimized this signature down to +// . +// +// The Requirement Machine instead emits +// . +// +// This is a hypothetical ABI break, but it is such a silly edge case that +// it shouldn't matter in practice. Given that either of the two conformance +// requirements here are redundant, the user can omit one or the other to +// specify the result that they desire. + +// expected-warning@+1 {{redundant conformance constraint 'U' : 'P2'}} func twoProtocols6(_: T, _: U) where U : P2, T.X == U, U.Y == T, T : P1 {} // CHECK-LABEL: twoProtocols6 -// CHECK: Generic signature: +// CHECK: Generic signature: diff --git a/test/Generics/rdar80503090.swift b/test/Generics/rdar80503090.swift index 0cb4584228ba7..e6a3d7636ff57 100644 --- a/test/Generics/rdar80503090.swift +++ b/test/Generics/rdar80503090.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=on // RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s // RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s -requirement-machine-inferred-signatures=on -disable-requirement-machine-concrete-contraction 2>&1 | %FileCheck %s diff --git a/test/Generics/requirement_inference.swift b/test/Generics/requirement_inference.swift index 41073317d8066..f674aeff1bfce 100644 --- a/test/Generics/requirement_inference.swift +++ b/test/Generics/requirement_inference.swift @@ -1,6 +1,5 @@ -// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=verify -// RUN: %target-typecheck-verify-swift -debug-generic-signatures > %t.dump -requirement-machine-protocol-signatures=verify 2>&1 -// RUN: %FileCheck %s < %t.dump +// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=verify -requirement-machine-inferred-signatures=off +// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=verify -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s protocol P1 { func p1() @@ -203,7 +202,7 @@ struct X8 : P12 { struct X9 where T.B == U.B { // CHECK-LABEL: X9.upperSameTypeConstraint - // CHECK: Generic signature: + // CHECK: Generic signature: // CHECK: Canonical generic signature: <τ_0_0, τ_0_1, τ_1_0 where τ_0_0 == X8, τ_0_1 : P12, τ_0_1.[P12]B == X7> func upperSameTypeConstraint(_: V) where T == X8 { } } @@ -219,7 +218,7 @@ struct X10: P11, P12 { struct X11 where T.B == U.B.A { // CHECK-LABEL: X11.upperSameTypeConstraint - // CHECK: Generic signature: + // CHECK: Generic signature: // CHECK: Canonical generic signature: <τ_0_0, τ_0_1, τ_1_0 where τ_0_0 : P12, τ_0_1 == X10, τ_0_0.[P12]B == X10> func upperSameTypeConstraint(_: V) where U == X10 { } } @@ -270,7 +269,7 @@ struct X18: P18, P17 { } // CHECK-LABEL: .X19.foo@ -// CHECK: Generic signature: +// CHECK: Generic signature: struct X19 where T == T.A { func foo(_: U) where T == X18 { } } diff --git a/test/ModuleInterface/access-filter.swift b/test/ModuleInterface/access-filter.swift index 2551434831cc8..d5e44a7684a4e 100644 --- a/test/ModuleInterface/access-filter.swift +++ b/test/ModuleInterface/access-filter.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t.swiftinterface %s -module-name AccessFilter +// RUN: %target-swift-frontend -typecheck -emit-module-interface-path %t.swiftinterface %s -module-name AccessFilter -requirement-machine-inferred-signatures=on // RUN: %FileCheck %s < %t.swiftinterface // RUN: %FileCheck -check-prefix NEGATIVE %s < %t.swiftinterface @@ -245,12 +245,12 @@ internal typealias InternalAlias_BAD = PublicAliasBase internal typealias ReallyInternalAlias_BAD = ReallyInternalAliasBase_BAD -// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.PublicAlias {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.PublicAliasBase {{[{]$}} extension GenericStruct where T == PublicAlias { // CHECK-NEXT: public func constrainedToPublicAlias(){{$}} public func constrainedToPublicAlias() {} } // CHECK-NEXT: {{^[}]$}} -// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.UFIAlias {{[{]$}} +// CHECK: extension AccessFilter.GenericStruct where T == AccessFilter.PublicAliasBase {{[{]$}} extension GenericStruct where T == UFIAlias { // CHECK-NEXT: @usableFromInline{{$}} // CHECK-NEXT: internal func constrainedToUFIAlias(){{$}} diff --git a/test/ModuleInterface/specialized-extension.swift b/test/ModuleInterface/specialized-extension.swift index 7bd85ba9b8665..bf1d76553a92c 100644 --- a/test/ModuleInterface/specialized-extension.swift +++ b/test/ModuleInterface/specialized-extension.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -module-name Test -typecheck -emit-module-interface-path - -enable-experimental-bound-generic-extensions %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name Test -typecheck -emit-module-interface-path - -enable-experimental-bound-generic-extensions %s -requirement-machine-inferred-signatures=on | %FileCheck %s public struct Tree { public struct Branch { @@ -23,7 +23,7 @@ extension Tree.Branch.Nest.Egg { public static func twoot() {} } // CHECK: } extension Tree.Branch.Nest.Egg { public static func twote() {} } -// CHECK: extension Test.Tree.Branch.Nest.Egg where T == Swift.Int, B == Swift.String, N == Swift.Void { +// CHECK: extension Test.Tree.Branch.Nest.Egg where T == Swift.Int, B == Swift.String, N == () { // CHECK: public static func twite() // CHECK: } extension Tree.Branch.Nest.Egg { public static func twite() {} } diff --git a/test/Serialization/Recovery/typedefs.swift b/test/Serialization/Recovery/typedefs.swift index 18405e952fe79..5aaa8f84bab07 100644 --- a/test/Serialization/Recovery/typedefs.swift +++ b/test/Serialization/Recovery/typedefs.swift @@ -2,7 +2,7 @@ // Cannot use -parse-as-library here because that would compile also the // #if VERIFY path, which contains top-level code. -// RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck -check-prefix CHECK-VTABLE %s +// RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module -enable-objc-interop %s -requirement-machine-inferred-signatures=on | %FileCheck -check-prefix CHECK-VTABLE %s // RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s @@ -126,7 +126,7 @@ open class User { // CHECK-RECOVERY: /* placeholder for returnsWrappedMethod() (vtable entries: 1) */ public func returnsWrappedMethod() -> WrappedInt { fatalError() } - // CHECK: func constrainedUnwrapped(_: T) where T : HasAssoc, T.Assoc == UnwrappedInt + // CHECK: func constrainedUnwrapped(_: T) where T : HasAssoc, T.Assoc == Int32 // CHECK-RECOVERY: func constrainedUnwrapped(_: T) where T : HasAssoc, T.Assoc == Int32 public func constrainedUnwrapped(_: T) where T.Assoc == UnwrappedInt { fatalError() } // CHECK: func constrainedWrapped(_: T) where T : HasAssoc, T.Assoc == WrappedInt diff --git a/test/attr/attr_specialize.swift b/test/attr/attr_specialize.swift index 8a19db851bfed..ce06160699383 100644 --- a/test/attr/attr_specialize.swift +++ b/test/attr/attr_specialize.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=verify // RUN: %target-swift-ide-test -print-ast-typechecked -source-filename=%s -disable-objc-attr-requires-foundation-module -define-availability 'SwiftStdlib 5.1:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0' | %FileCheck %s struct S {} @@ -54,8 +54,10 @@ class G { // expected-error@-1 {{too few generic parameters are specified in '_specialize' attribute (got 0, but expected 1)}} // expected-note@-2 {{missing constraint for 'T' in '_specialize' attribute}} @_specialize(where T == S) // expected-error{{same-type constraint 'T' == 'S' is recursive}} - // expected-error@-1 {{too few generic parameters are specified in '_specialize' attribute (got 0, but expected 1)}} - // expected-note@-2 {{missing constraint for 'T' in '_specialize' attribute}} + // expected-error@-1 {{cannot build rewrite system for generic signature; concrete nesting limit exceeded}} + // expected-note@-2 {{failed rewrite rule is τ_0_0.[concrete: S>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] => τ_0_0}} + // expected-error@-3 {{too few generic parameters are specified in '_specialize' attribute (got 0, but expected 1)}} + // expected-note@-4 {{missing constraint for 'T' in '_specialize' attribute}} @_specialize(where T == Int, U == Int) // expected-error{{cannot find type 'U' in scope}} func noGenericParams() {} diff --git a/validation-test/compiler_crashers_2_fixed/sr9584.swift b/validation-test/compiler_crashers_2_fixed/sr9584.swift index 04a22340f5827..2f78bda682e8b 100644 --- a/validation-test/compiler_crashers_2_fixed/sr9584.swift +++ b/validation-test/compiler_crashers_2_fixed/sr9584.swift @@ -1,4 +1,4 @@ -// RUN: not %target-swift-frontend -typecheck %s +// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=on struct S {} @@ -9,6 +9,10 @@ protocol P { extension S: P where N: P { static func f(_ x: X) -> S where A == X, X.A == N { + // expected-error@-1 {{cannot build rewrite system for generic signature; rule length limit exceeded}} + // expected-note@-2 {{failed rewrite rule is τ_0_0.[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[concrete: S>>>>>>>>>>>] => τ_0_0.[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A].[P:A] [subst↓]}} + // expected-error@-3 {{'A' is not a member type of type 'X'}} + // expected-error@-4 {{'A' is not a member type of type 'X'}} return S() } } From db801a5fdfa0e7e0439cb42198dfcd9f37b0307a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sun, 13 Mar 2022 23:29:34 -0400 Subject: [PATCH 041/242] RequirementMachine: Add redundant requirements diagnostic test that used to fail with the GSB For some reason we didn't diagnose some of the redundancies here; the new logic in the Requirement Machine works just fine though! https://bugs.swift.org/browse/SR-14917 / rdar://problem/80820294 --- test/Generics/sr14917.swift | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test/Generics/sr14917.swift diff --git a/test/Generics/sr14917.swift b/test/Generics/sr14917.swift new file mode 100644 index 0000000000000..6d5f3c2cc9e15 --- /dev/null +++ b/test/Generics/sr14917.swift @@ -0,0 +1,27 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=on + +protocol P { + associatedtype A + associatedtype AS: Q where AS.B == A +} + +protocol Q { + associatedtype B +} + +struct S1 where T.AS.B == T.A {} +// expected-warning@-1 {{redundant same-type constraint 'T.AS.B' == 'T.A'}} + +struct S2 { + struct Nested where T.AS.B == T.A {} + // expected-warning@-1 {{redundant same-type constraint 'T.AS.B' == 'T.A'}} +} + +extension S2 where T.AS.B == T.A {} +// expected-warning@-1 {{redundant same-type constraint 'T.AS.B' == 'T.A'}} + +extension P where AS.B == A {} +// expected-warning@-1 {{redundant same-type constraint 'Self.AS.B' == 'Self.A'}} + +extension P where Self : P {} +// expected-warning@-1 {{redundant conformance constraint 'Self' : 'P'}} From 2ee5aeefeeb24229e2b8aad3829d2a11ab4cad92 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 00:13:19 -0400 Subject: [PATCH 042/242] RequirementMachine: InferredGenericSignatureRequest returns parent generic signature if completion fails --- lib/AST/RequirementMachine/RequirementMachineRequests.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp index 44c79741e327c..5c2d1d8e33e83 100644 --- a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp +++ b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp @@ -522,7 +522,8 @@ InferredGenericSignatureRequestRQM::evaluate( diag::requirement_machine_completion_rule, rule); - auto result = GenericSignature::get(genericParams, {}); + auto result = GenericSignature::get(genericParams, + parentSig.getRequirements()); return GenericSignatureWithError( result, GenericSignatureErrorFlags::CompletionFailed); } From a61f67a0e8c29fd1fb29ec8b919ffd10b427bcca Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 01:03:46 -0400 Subject: [PATCH 043/242] RequirementMachine: Hack to allow protocol typealiases to appear in associated type inheritance clauses If you have something like protocol P { typealias A = C associatedtype T : A } class C {} Then ::Structural resolution of 'A' in the inheritance clause will produce a DependentMemberType 'Self.A'. Check for this case and attempt ::Interface resolution to get the correct underlying type. Fixes rdar://problem/90219229. --- .../RequirementLowering.cpp | 30 +++++++++++++++- test/Generics/rdar90219229.swift | 36 +++++++++++++++++++ .../28839-genericsig.swift | 2 +- 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 test/Generics/rdar90219229.swift diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index d9848a7dbd2e0..459a21a10c30d 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -504,10 +504,38 @@ void swift::rewriting::realizeInheritedRequirements( Type inheritedType = evaluateOrDefault(ctx.evaluator, InheritedTypeRequest{decl, index, - TypeResolutionStage::Structural}, + TypeResolutionStage::Structural}, Type()); if (!inheritedType) continue; + // The GenericSignatureBuilder allowed an associated type's inheritance + // clause to reference a protocol typealias whose underlying type was a + // protocol or class. + // + // Since protocol typealiases resolve to DependentMemberTypes in + // ::Structural mode, this relied on the GSB's "delayed requirements" + // mechanism. + // + // The RequirementMachine does not have an equivalent, and cannot really + // support that because we need to collect the protocols mentioned on + // the right hand sides of conformance requirements ahead of time. + // + // However, we can support it in simple cases where the typealias is + // defined in the protocol itself and is accessed as a member of 'Self'. + if (auto *assocTypeDecl = dyn_cast(decl)) { + if (auto memberType = inheritedType->getAs()) { + if (memberType->getBase()->isEqual( + assocTypeDecl->getProtocol()->getSelfInterfaceType())) { + inheritedType + = evaluateOrDefault(ctx.evaluator, + InheritedTypeRequest{decl, index, + TypeResolutionStage::Interface}, + Type()); + if (!inheritedType) continue; + } + } + } + auto *typeRepr = inheritedTypes[index].getTypeRepr(); SourceLoc loc = (typeRepr ? typeRepr->getStartLoc() : SourceLoc()); if (moduleForInference) { diff --git a/test/Generics/rdar90219229.swift b/test/Generics/rdar90219229.swift new file mode 100644 index 0000000000000..197de22f178d8 --- /dev/null +++ b/test/Generics/rdar90219229.swift @@ -0,0 +1,36 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on +// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s + +// CHECK-LABEL: .P@ +// CHECK-NEXT: Requirement signature: +protocol P { + typealias A = C + + associatedtype T : A +} + +class C {} + +protocol PBad { + typealias A = C + + associatedtype B : P + + associatedtype T1 : B.A + // expected-error@-1 {{type 'Self.T1' constrained to non-protocol, non-class type 'Self.B.A'}} + + associatedtype T2 where T2 : A + // expected-error@-1 {{type 'Self.T2' constrained to non-protocol, non-class type 'Self.A'}} +} + +// FIXME: Terrible diagnostics. + +protocol PWorse { +// expected-error@-1 2{{circular reference}} +// expected-note@-2 4{{through reference here}} + typealias A = C + + associatedtype T : Self.A +// expected-note@-1 2{{while resolving type 'Self.A'}} +// expected-note@-2 2{{through reference here}} +} \ No newline at end of file diff --git a/validation-test/compiler_crashers_fixed/28839-genericsig.swift b/validation-test/compiler_crashers_fixed/28839-genericsig.swift index cd2fac70f06ae..c33ac235d9ec6 100644 --- a/validation-test/compiler_crashers_fixed/28839-genericsig.swift +++ b/validation-test/compiler_crashers_fixed/28839-genericsig.swift @@ -6,7 +6,7 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// RUN: not %target-swift-frontend %s -emit-ir +// RUN: not %target-swift-frontend %s -emit-ir -requirement-machine-protocol-signatures=on class a{ class a Date: Mon, 14 Mar 2022 01:24:36 -0400 Subject: [PATCH 044/242] RequirementMachine: Skip 'verify' check if completion failed We obviously don't have a valid generic signature in this case. --- lib/AST/GenericSignatureBuilder.cpp | 3 +++ test/decl/protocol/req/recursion.swift | 18 +++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index e44261d3bc345..f205b7745a5a9 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -8276,6 +8276,7 @@ AbstractGenericSignatureRequest::evaluate( return rqmResult; if (!rqmResult.getInt().contains(GenericSignatureErrorFlags::HasConflict) && + !rqmResult.getInt().contains(GenericSignatureErrorFlags::CompletionFailed) && !rqmResult.getPointer()->isEqual(gsbResult.getPointer())) { PrintOptions opts; opts.ProtocolQualifiedDependentMemberTypes = true; @@ -8489,6 +8490,7 @@ InferredGenericSignatureRequest::evaluate( return rqmResult; if (!rqmResult.getInt().contains(GenericSignatureErrorFlags::HasConflict) && + !rqmResult.getInt().contains(GenericSignatureErrorFlags::CompletionFailed) && !rqmResult.getPointer()->isEqual(gsbResult.getPointer())) { PrintOptions opts; opts.ProtocolQualifiedDependentMemberTypes = true; @@ -8692,6 +8694,7 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator, auto gsbResult = buildViaGSB(); if (!rqmResult.getErrors().contains(GenericSignatureErrorFlags::HasConflict) && + !rqmResult.getErrors().contains(GenericSignatureErrorFlags::CompletionFailed) && !compare(rqmResult.getRequirements(), gsbResult.getRequirements())) { PrintOptions opts; diff --git a/test/decl/protocol/req/recursion.swift b/test/decl/protocol/req/recursion.swift index 17316ec1df55a..5e509c27f97f2 100644 --- a/test/decl/protocol/req/recursion.swift +++ b/test/decl/protocol/req/recursion.swift @@ -1,16 +1,20 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=verify protocol SomeProtocol { associatedtype T } extension SomeProtocol where T == Optional { } // expected-error{{same-type constraint 'Self.T' == 'Optional' is recursive}} +// expected-error@-1 {{cannot build rewrite system for generic signature; concrete nesting limit exceeded}} +// expected-note@-2 {{failed rewrite rule is τ_0_0.[SomeProtocol:T].[concrete: Optional>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] => τ_0_0.[SomeProtocol:T]}} // rdar://problem/19840527 class X where T == X { // expected-error{{same-type constraint 'T' == 'X' is recursive}} -// expected-error@-1{{same-type requirement makes generic parameter 'T' non-generic}} -// expected-error@-2 3{{generic class 'X' has self-referential generic requirements}} +// expected-error@-1 {{cannot build rewrite system for generic signature; concrete nesting limit exceeded}} +// expected-note@-2 {{failed rewrite rule is τ_0_0.[concrete: X>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] => τ_0_0}} +// expected-error@-3{{same-type requirement makes generic parameter 'T' non-generic}} +// expected-error@-4 6{{generic class 'X' has self-referential generic requirements}} var type: T { return Swift.type(of: self) } // expected-error{{cannot convert return expression of type 'X.Type' to return type 'T'}} } @@ -45,7 +49,7 @@ public protocol P { } public struct S where A.T == S { -// expected-error@-1 3{{generic struct 'S' has self-referential generic requirements}} +// expected-error@-1 6{{generic struct 'S' has self-referential generic requirements}} func f(a: A.T) { g(a: id(t: a)) // `a` has error type which is diagnosed as circular reference _ = A.T.self @@ -70,7 +74,7 @@ protocol PI { } struct SI : I where A : I, A.T == SI { -// expected-error@-1 3{{generic struct 'SI' has self-referential generic requirements}} +// expected-error@-1 6{{generic struct 'SI' has self-referential generic requirements}} func ggg(t: T.Type) -> T { return T() } @@ -97,9 +101,9 @@ struct S5 : I where A : I, A.T == S4 { } // Used to hit ArchetypeBuilder assertions struct SU where A.T == SU { -// expected-error@-1 3{{generic struct 'SU' has self-referential generic requirements}} +// expected-error@-1 6{{generic struct 'SU' has self-referential generic requirements}} } struct SIU : I where A : I, A.T == SIU { -// expected-error@-1 3{{generic struct 'SIU' has self-referential generic requirements}} +// expected-error@-1 6{{generic struct 'SIU' has self-referential generic requirements}} } From 7af8a35a0b5846d81e2075f93d75bdda8f3583b4 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 2 Mar 2022 08:29:11 -0800 Subject: [PATCH 045/242] [Gardening] Fixed diction. --- include/swift/SIL/Projection.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/swift/SIL/Projection.h b/include/swift/SIL/Projection.h index c0d57a0c0aece..a15ce9c9b55aa 100644 --- a/include/swift/SIL/Projection.h +++ b/include/swift/SIL/Projection.h @@ -484,7 +484,7 @@ static_assert(sizeof(Projection) == sizeof(uintptr_t), /// The main purpose of this class is to enable one to reason about iterated /// chains of projections. Some example usages are: /// -/// 1. Converting value projections to aggregate projections or vis-a-versa. +/// 1. Converting value projections to aggregate projections or vice versa. /// 2. Performing tuple operations on two paths (using the mathematical /// definition of tuples as ordered sets). class ProjectionPath { From 5774e5447acc5397902602989adfda294259fc05 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 2 Mar 2022 08:30:00 -0800 Subject: [PATCH 046/242] [Gardening] Silenced warning. --- lib/SIL/IR/SILInstructions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SIL/IR/SILInstructions.cpp b/lib/SIL/IR/SILInstructions.cpp index cb17936770c0c..29319d6f01692 100644 --- a/lib/SIL/IR/SILInstructions.cpp +++ b/lib/SIL/IR/SILInstructions.cpp @@ -75,7 +75,7 @@ static void buildTypeDependentOperands( bool hasDynamicSelf, SmallVectorImpl &TypeDependentOperands, SILFunction &F) { - for (const auto archetype : RootOpenedArchetypes) { + for (const auto &archetype : RootOpenedArchetypes) { SILValue def = F.getModule().getRootOpenedArchetypeDef(archetype, &F); assert(def->getFunction() == &F && "def of root opened archetype is in wrong function"); From cc56e13fc481b7ec5006da913b6fe048ffd56b07 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 4 Mar 2022 15:57:39 -0800 Subject: [PATCH 047/242] [Gardening] Addressed a couple typos. --- include/swift/SIL/MemoryLocations.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/swift/SIL/MemoryLocations.h b/include/swift/SIL/MemoryLocations.h index 74bc32f8cd7b8..0d3c458b7c564 100644 --- a/include/swift/SIL/MemoryLocations.h +++ b/include/swift/SIL/MemoryLocations.h @@ -238,14 +238,14 @@ class MemoryLocations { addr2LocIdx[projection] = locIdx; } - /// Sets the location bits os \p addr in \p bits, if \p addr is associated + /// Sets the location bits of \p addr in \p bits, if \p addr is associated /// with a location. void setBits(Bits &bits, SILValue addr) const { if (auto *loc = getLocation(addr)) bits |= loc->subLocations; } - /// Clears the location bits os \p addr in \p bits, if \p addr is associated + /// Clears the location bits of \p addr in \p bits, if \p addr is associated /// with a location. void clearBits(Bits &bits, SILValue addr) const { if (auto *loc = getLocation(addr)) @@ -307,7 +307,7 @@ class MemoryLocations { SubLocationMap &subLocationMap); /// Helper function called by analyzeLocation to create a sub-location for - /// and address projection and check all of its uses. + /// an address projection and check all of its uses. bool analyzeAddrProjection( SingleValueInstruction *projection, unsigned parentLocIdx,unsigned fieldNr, SmallVectorImpl &collectedVals, SubLocationMap &subLocationMap); From c2d80cdbc3dcb53482a5f83572f2b6906714c173 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 4 Mar 2022 18:11:42 -0800 Subject: [PATCH 048/242] [AccessPath] Gave PathNode pointer-like traits. --- include/swift/SIL/MemAccessUtils.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index eef1e8c335045..3f021dc355399 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -1267,6 +1267,18 @@ template <> struct DenseMapInfo { } }; +// Allow AccessPath::PathNode to be used as a pointer-like template argument. +template<> +struct PointerLikeTypeTraits { + static inline void *getAsVoidPointer(swift::AccessPath::PathNode node) { + return (void *)node.node; + } + static inline swift::AccessPath::PathNode getFromVoidPointer(void *pointer) { + return swift::AccessPath::PathNode((swift::IndexTrieNode *)pointer); + } + enum { NumLowBitsAvailable = + PointerLikeTypeTraits::NumLowBitsAvailable }; +}; } // end namespace llvm //===----------------------------------------------------------------------===// From 6b6e585d5432e492f93212491be545e94b5d4f2c Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 4 Mar 2022 18:39:50 -0800 Subject: [PATCH 049/242] [MemAccessUtils] Visit product type tree of addr. Added a function that visits the leaves of the type/projection tree of the specified address and calls its visitor with the path node to and type of each. --- include/swift/SIL/MemAccessUtils.h | 10 ++++++++++ lib/SIL/Utils/MemAccessUtils.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index 3f021dc355399..e40f811f1e6be 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -1209,6 +1209,16 @@ struct AccessPathWithBase { void dump() const; }; +// Visits all the "product leaves" of the type tree of the specified value and +// invokes provided visitor, identifying the leaf by its path node and +// providing its type. +// +// The "product leaves" are the leaves obtained by only looking through type +// products (structs and tuples) and NOT type sums (enums). +void visitProductLeafAccessPathNodes( + SILValue address, TypeExpansionContext tec, SILModule &module, + std::function visitor); + inline AccessPath AccessPath::compute(SILValue address) { return AccessPathWithBase::compute(address).accessPath; } diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 52265ba59d924..1047177764554 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -1357,6 +1357,36 @@ AccessPathWithBase AccessPathWithBase::computeInScope(SILValue address) { .findAccessPath(address); } +void swift::visitProductLeafAccessPathNodes( + SILValue address, TypeExpansionContext tec, SILModule &module, + std::function visitor) { + SmallVector, 32> worklist; + auto rootPath = AccessPath::compute(address); + auto *node = rootPath.getPathNode().node; + worklist.push_back({address->getType(), node}); + while (!worklist.empty()) { + auto pair = worklist.pop_back_val(); + auto silType = pair.first; + auto *node = pair.second; + if (auto tupleType = silType.getAs()) { + for (unsigned index : indices(tupleType->getElements())) { + auto *elementNode = node->getChild(index); + worklist.push_back({silType.getTupleElementType(index), elementNode}); + } + } else if (auto *decl = silType.getStructOrBoundGenericStruct()) { + unsigned index = 0; + for (auto *field : decl->getStoredProperties()) { + auto *fieldNode = node->getChild(index); + worklist.push_back( + {silType.getFieldType(field, module, tec), fieldNode}); + ++index; + } + } else { + visitor(AccessPath::PathNode(node), silType); + } + } +} + void AccessPath::Index::print(raw_ostream &os) const { if (isSubObjectProjection()) os << '#' << getSubObjectIndex(); From 8a0e5cd14632430381913a5ca6b0d40ae12dfaf4 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 9 Mar 2022 18:33:23 -0800 Subject: [PATCH 050/242] [MemAccessUtils] Distinguished cast flavors. --- include/swift/SIL/MemAccessUtils.h | 53 ++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index e40f811f1e6be..d9d407ce921b9 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -1523,28 +1523,14 @@ inline Operand *getAccessProjectionOperand(SingleValueInstruction *svi) { }; } -/// An address, pointer, or box cast that occurs outside of the formal -/// access. These convert the base of accessed storage without affecting the -/// AccessPath. Useful for both use-def and def-use traversal. The source -/// address must be at operand(0). -/// -/// Some of these casts, such as address_to_pointer, may also occur inside of a -/// formal access. -/// -/// TODO: Add stricter structural guarantee such that these never -/// occur within an access. It's important to be able to get the accessed -/// address without looking though type casts or pointer_to_address [strict], -/// which we can't do if those operations are behind access projections. -inline bool isAccessStorageCast(SingleValueInstruction *svi) { +/// A cast for the purposes of AccessStorage which may change the +/// underlying type but doesn't affect the AccessPath. See isAccessStorageCast. +inline bool isAccessStorageTypeCast(SingleValueInstruction *svi) { switch (svi->getKind()) { default: return false; - - // Simply pass-thru the incoming address. - case SILInstructionKind::MarkUninitializedInst: + // Simply pass-thru the incoming address. But change its type! case SILInstructionKind::UncheckedAddrCastInst: - case SILInstructionKind::MarkDependenceInst: - case SILInstructionKind::CopyValueInst: // Casting to RawPointer does not affect the AccessPath. When converting // between address types, they must be layout compatible (with truncation). case SILInstructionKind::AddressToPointerInst: @@ -1574,6 +1560,37 @@ inline bool isAccessStorageCast(SingleValueInstruction *svi) { } } +/// A cast for the purposes of AccessStorage which doesn't change the +/// underlying type and doesn't affect the AccessPath. See isAccessStorageCast. +inline bool isAccessStorageIdentityCast(SingleValueInstruction *svi) { + switch (svi->getKind()) { + default: + return false; + + // Simply pass-thru the incoming address. + case SILInstructionKind::MarkUninitializedInst: + case SILInstructionKind::MarkDependenceInst: + case SILInstructionKind::CopyValueInst: + return true; + } +} + +/// An address, pointer, or box cast that occurs outside of the formal +/// access. These convert the base of accessed storage without affecting the +/// AccessPath. Useful for both use-def and def-use traversal. The source +/// address must be at operand(0). +/// +/// Some of these casts, such as address_to_pointer, may also occur inside of a +/// formal access. +/// +/// TODO: Add stricter structural guarantee such that these never +/// occur within an access. It's important to be able to get the accessed +/// address without looking though type casts or pointer_to_address [strict], +/// which we can't do if those operations are behind access projections. +inline bool isAccessStorageCast(SingleValueInstruction *svi) { + return isAccessStorageTypeCast(svi) || isAccessStorageIdentityCast(svi); +} + /// Abstract CRTP class for a visiting instructions that are part of the use-def /// chain from an accessed address up to the storage base. /// From a1c1b32a8ce7d8508310fe4a4e400ffd0506c3dd Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 9 Mar 2022 18:36:06 -0800 Subject: [PATCH 051/242] [MemAccessUtils] Added RelativeAccessStorageWithBase. The new "relative" version of AccessStorageWithBase carries additional information about the walk from the specified address back to the base. For now, that includes the original address and the most transformative sort of cast that was encountered. --- include/swift/SIL/MemAccessUtils.h | 50 +++++++++++++++++-- lib/SIL/Utils/MemAccessUtils.cpp | 48 ++++++++++++++---- .../SemanticARC/LoadCopyToLoadBorrowOpt.cpp | 2 +- 3 files changed, 83 insertions(+), 17 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index d9d407ce921b9..9d3b90ce11384 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -260,6 +260,16 @@ enum class NestedAccessType { StopAtAccessBegin, IgnoreAccessBegin }; /// previous. enum class AccessUseType { Exact, Inner, Overlapping }; +/// When walking from a value to its storage, casts may be encountered. The +/// cases describe variety of encountered casts, categorized by the kind of +/// transformation that the casts perform. +/// +/// The enum values are ordered. Each successive cast kind is more +/// transformative than the last. +/// +/// TODO: Distinguish between LayoutEquivalent and LayoutCompatibile. +enum class AccessStorageCast { Identity, Type }; + /// The physical representation used to identify access information and common /// API used by both AccessBase and AccessStorage. /// @@ -898,6 +908,30 @@ struct AccessStorageWithBase { void dump() const; }; +/// Extends AccessStorageWithBase by adding information that was obtained while +/// visiting from a particular address, to which an instance of this is +/// relative. +struct RelativeAccessStorageWithBase { + + /// Identical to AccessStorageWithBase::compute but preserves information + /// specific to the walk from address; + static RelativeAccessStorageWithBase compute(SILValue address); + + /// Identical to AccessStorageWithBase::computeInScope but preserves + /// information specific to the walk from address; + static RelativeAccessStorageWithBase computeInScope(SILValue address); + + /// The address to which this RelativeAccessStorageWithBase is relative. + SILValue address; + /// The underlying access storage and base. + AccessStorageWithBase storageWithBase; + /// The most transformative cast that was seen between when walking from + /// address to storage.base; + Optional cast; + + AccessStorage getStorage() const { return storageWithBase.storage; } +}; + /// Return an AccessStorage value that identifies formally accessed storage /// for \p beginAccess, considering any outer access scope as having distinct /// storage from this access scope. This is useful for exclusivity checking @@ -1637,8 +1671,9 @@ class AccessUseDefChainVisitor { // Result visitBase(SILValue base, AccessStorage::Kind kind); // Result visitNonAccess(SILValue base); // Result visitPhi(SILPhiArgument *phi); - // Result visitStorageCast(SingleValueInstruction *cast, Operand *sourceOper); - // Result visitAccessProjection(SingleValueInstruction *projectedAddr, + // Result visitStorageCast(SingleValueInstruction *cast, Operand *sourceOper, + // AccessStorageCast cast); Result + // visitAccessProjection(SingleValueInstruction *projectedAddr, // Operand *sourceOper); Result visit(SILValue sourceAddr); @@ -1650,8 +1685,12 @@ Result AccessUseDefChainVisitor::visit(SILValue sourceAddr) { if (auto *projOper = getAccessProjectionOperand(svi)) return asImpl().visitAccessProjection(svi, projOper); - if (isAccessStorageCast(svi)) - return asImpl().visitStorageCast(svi, &svi->getAllOperands()[0]); + if (isAccessStorageTypeCast(svi)) + return asImpl().visitStorageCast(svi, &svi->getAllOperands()[0], + AccessStorageCast::Type); + if (isAccessStorageIdentityCast(svi)) + return asImpl().visitStorageCast(svi, &svi->getAllOperands()[0], + AccessStorageCast::Identity); } switch (sourceAddr->getKind()) { default: @@ -1826,7 +1865,8 @@ class AccessUseDefChainCloner return SILValue(); } - SILValue visitStorageCast(SingleValueInstruction *cast, Operand *sourceOper) { + SILValue visitStorageCast(SingleValueInstruction *cast, Operand *sourceOper, + AccessStorageCast) { // The cloner does not currently know how to create compensating // end_borrows or fix mark_dependence operands. if (isa(cast) || isa(cast)) diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 1047177764554..03b908f9c989e 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -125,7 +125,8 @@ class AccessPhiVisitor phiArg->getIncomingPhiValues(pointerWorklist); } - void visitStorageCast(SingleValueInstruction *cast, Operand *sourceOper) { + void visitStorageCast(SingleValueInstruction *cast, Operand *sourceOper, + AccessStorageCast) { // Allow conversions to/from pointers and addresses on disjoint phi paths // only if the underlying useDefVisitor allows it. if (storageCastTy == IgnoreStorageCast) @@ -209,7 +210,8 @@ class FindAccessVisitorImpl : public AccessUseDefChainVisitor { return this->asImpl().visitNonAccess(phiArg); } - SILValue visitStorageCast(SingleValueInstruction *, Operand *sourceAddr) { + SILValue visitStorageCast(SingleValueInstruction *, Operand *sourceAddr, + AccessStorageCast cast) { assert(storageCastTy == IgnoreStorageCast); return sourceAddr->get(); } @@ -331,11 +333,12 @@ class FindAccessBaseVisitor } // Override visitStorageCast to avoid seeing through arbitrary address casts. - SILValue visitStorageCast(SingleValueInstruction *cast, Operand *sourceAddr) { + SILValue visitStorageCast(SingleValueInstruction *svi, Operand *sourceAddr, + AccessStorageCast cast) { if (storageCastTy == StopAtStorageCast) - return visitNonAccess(cast); + return visitNonAccess(svi); - return SuperTy::visitStorageCast(cast, sourceAddr); + return SuperTy::visitStorageCast(svi, sourceAddr, cast); } }; @@ -1062,11 +1065,13 @@ namespace { // AccessStorage object for all projection paths. class FindAccessStorageVisitor : public FindAccessVisitorImpl { + using SuperTy = FindAccessVisitorImpl; public: struct Result { Optional storage; SILValue base; + Optional seenCast; }; private: @@ -1102,6 +1107,8 @@ class FindAccessStorageVisitor // may be multiple global_addr bases for identical storage. SILValue getBase() const { return result.base; } + Optional getCast() const { return result.seenCast; } + // MARK: AccessPhiVisitor::UseDefVisitor implementation. // A valid result requires valid storage, but not a valid base. @@ -1128,22 +1135,41 @@ class FindAccessStorageVisitor invalidateResult(); return SILValue(); } + + SILValue visitStorageCast(SingleValueInstruction *svi, Operand *sourceOper, + AccessStorageCast cast) { + result.seenCast = result.seenCast ? std::max(*result.seenCast, cast) : cast; + return SuperTy::visitStorageCast(svi, sourceOper, cast); + } }; } // end anonymous namespace +RelativeAccessStorageWithBase +RelativeAccessStorageWithBase::compute(SILValue address) { + FindAccessStorageVisitor visitor(NestedAccessType::IgnoreAccessBegin); + visitor.findStorage(address); + return { + address, {visitor.getStorage(), visitor.getBase()}, visitor.getCast()}; +} + +RelativeAccessStorageWithBase +RelativeAccessStorageWithBase::computeInScope(SILValue address) { + FindAccessStorageVisitor visitor(NestedAccessType::StopAtAccessBegin); + visitor.findStorage(address); + return { + address, {visitor.getStorage(), visitor.getBase()}, visitor.getCast()}; +} + AccessStorageWithBase AccessStorageWithBase::compute(SILValue sourceAddress) { - FindAccessStorageVisitor visitor(NestedAccessType::IgnoreAccessBegin); - visitor.findStorage(sourceAddress); - return {visitor.getStorage(), visitor.getBase()}; + return RelativeAccessStorageWithBase::compute(sourceAddress).storageWithBase; } AccessStorageWithBase AccessStorageWithBase::computeInScope(SILValue sourceAddress) { - FindAccessStorageVisitor visitor(NestedAccessType::StopAtAccessBegin); - visitor.findStorage(sourceAddress); - return {visitor.getStorage(), visitor.getBase()}; + return RelativeAccessStorageWithBase::computeInScope(sourceAddress) + .storageWithBase; } AccessStorage AccessStorage::compute(SILValue sourceAddress) { diff --git a/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp b/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp index 6e410f9b78991..c531fc099821c 100644 --- a/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp +++ b/lib/SILOptimizer/SemanticARC/LoadCopyToLoadBorrowOpt.cpp @@ -264,7 +264,7 @@ class StorageGuaranteesLoadVisitor } void visitStorageCast(SingleValueInstruction *projectedAddr, - Operand *parentAddr) { + Operand *parentAddr, AccessStorageCast cast) { return next(parentAddr->get()); } From 78a410c19e0253c35c1f11a3e5601635299247b1 Mon Sep 17 00:00:00 2001 From: Alex <1010788+bulbazord@users.noreply.github.com> Date: Mon, 14 Mar 2022 09:55:03 -0700 Subject: [PATCH 052/242] [cxx-interop] Add support for operator! overloading (#41434) This patch enables operator overloading for operator! when cxx interop is enabled. --- lib/ClangImporter/ImportName.cpp | 1 + test/Interop/Cxx/operators/Inputs/member-inline.h | 7 +++++++ .../Cxx/operators/member-inline-module-interface.swift | 4 ++++ test/Interop/Cxx/operators/member-inline-silgen.swift | 10 ++++++++++ .../Cxx/operators/member-inline-typechecker.swift | 3 +++ test/Interop/Cxx/operators/member-inline.swift | 7 +++++++ 6 files changed, 32 insertions(+) diff --git a/lib/ClangImporter/ImportName.cpp b/lib/ClangImporter/ImportName.cpp index 29d0886122df6..72f88f3af3e9d 100644 --- a/lib/ClangImporter/ImportName.cpp +++ b/lib/ClangImporter/ImportName.cpp @@ -1797,6 +1797,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D, case clang::OverloadedOperatorKind::OO_Caret: case clang::OverloadedOperatorKind::OO_Amp: case clang::OverloadedOperatorKind::OO_Pipe: + case clang::OverloadedOperatorKind::OO_Exclaim: case clang::OverloadedOperatorKind::OO_Less: case clang::OverloadedOperatorKind::OO_Greater: case clang::OverloadedOperatorKind::OO_LessLess: diff --git a/test/Interop/Cxx/operators/Inputs/member-inline.h b/test/Interop/Cxx/operators/Inputs/member-inline.h index 2fc5dd19090ce..04459a3183518 100644 --- a/test/Interop/Cxx/operators/Inputs/member-inline.h +++ b/test/Interop/Cxx/operators/Inputs/member-inline.h @@ -21,6 +21,13 @@ struct LoadableIntWrapper { } }; +struct LoadableBoolWrapper { + bool value; + LoadableBoolWrapper operator!() { + return LoadableBoolWrapper{.value = !value}; + } +}; + struct AddressOnlyIntWrapper { int value; diff --git a/test/Interop/Cxx/operators/member-inline-module-interface.swift b/test/Interop/Cxx/operators/member-inline-module-interface.swift index eb12cb2ba41f9..02c65d8f2beee 100644 --- a/test/Interop/Cxx/operators/member-inline-module-interface.swift +++ b/test/Interop/Cxx/operators/member-inline-module-interface.swift @@ -7,6 +7,10 @@ // CHECK: mutating func callAsFunction(_ x: Int32, _ y: Int32) -> Int32 // CHECK: } +// CHECK: struct LoadableBoolWrapper { +// CHECK: static func ! (lhs: inout LoadableBoolWrapper) -> LoadableBoolWrapper +// CHECK: } + // CHECK: struct AddressOnlyIntWrapper { // CHECK: mutating func callAsFunction() -> Int32 // CHECK: mutating func callAsFunction(_ x: Int32) -> Int32 diff --git a/test/Interop/Cxx/operators/member-inline-silgen.swift b/test/Interop/Cxx/operators/member-inline-silgen.swift index 1a1269918abb3..afff4c336d99c 100644 --- a/test/Interop/Cxx/operators/member-inline-silgen.swift +++ b/test/Interop/Cxx/operators/member-inline-silgen.swift @@ -13,6 +13,16 @@ public func sub(_ lhs: inout LoadableIntWrapper, _ rhs: LoadableIntWrapper) -> L // CHECK: sil [clang LoadableIntWrapper."-"] [[NAME]] : $@convention(c) (@inout LoadableIntWrapper, LoadableIntWrapper) -> LoadableIntWrapper +public func exclaim(_ wrapper: inout LoadableBoolWrapper) -> LoadableBoolWrapper { !wrapper } + +// CHECK: bb0([[SELF:%.*]] : $*LoadableBoolWrapper): +// CHECK: [[SELFACCESS:%.*]] = begin_access [modify] [static] [[SELF]] : $*LoadableBoolWrapper +// CHECK: [[OP:%.*]] = function_ref [[NAME:@(_ZN19LoadableBoolWrapperntEv|\?\?7LoadableBoolWrapper@@QEAA\?AU0@XZ)]] : $@convention(c) (@inout LoadableBoolWrapper) -> LoadableBoolWrapper +// CHECK: apply [[OP]]([[SELFACCESS]]) : $@convention(c) (@inout LoadableBoolWrapper) -> LoadableBoolWrapper +// CHECK: end_access [[SELFACCESS]] + +// CHECK: sil [clang LoadableBoolWrapper."!"] [[NAME]] : $@convention(c) (@inout LoadableBoolWrapper) -> LoadableBoolWrapper + public func call(_ wrapper: inout LoadableIntWrapper, _ arg: Int32) -> Int32 { wrapper(arg) } // CHECK: bb0([[SELF:%.*]] : $*LoadableIntWrapper, [[RHS:%.*]] : $Int32): diff --git a/test/Interop/Cxx/operators/member-inline-typechecker.swift b/test/Interop/Cxx/operators/member-inline-typechecker.swift index 5714c1de67b68..f3fcc873dc179 100644 --- a/test/Interop/Cxx/operators/member-inline-typechecker.swift +++ b/test/Interop/Cxx/operators/member-inline-typechecker.swift @@ -10,6 +10,9 @@ let resultCall0 = lhs() let resultCall1 = lhs(1) let resultCall2 = lhs(1, 2) +var boolWrapper = LoadableBoolWrapper(value: true) +let notBoolResult = !boolWrapper + var addressOnly = AddressOnlyIntWrapper(42) let addressOnlyResultCall0 = addressOnly() diff --git a/test/Interop/Cxx/operators/member-inline.swift b/test/Interop/Cxx/operators/member-inline.swift index 06818d4ebd9c2..5f323b255bf71 100644 --- a/test/Interop/Cxx/operators/member-inline.swift +++ b/test/Interop/Cxx/operators/member-inline.swift @@ -31,6 +31,13 @@ OperatorsTestSuite.test("LoadableIntWrapper.call (inline)") { expectEqual(57, resultTwoArgs) } +OperatorsTestSuite.test("LoadableBoolWrapper.exclaim (inline)") { + var wrapper = LoadableBoolWrapper(value: true) + + let resultExclaim = !wrapper + expectEqual(false, resultExclaim.value) +} + OperatorsTestSuite.test("AddressOnlyIntWrapper.call (inline)") { var wrapper = AddressOnlyIntWrapper(42) From 93c4c621b71c938f3a299aed4e13d4a3f80fa614 Mon Sep 17 00:00:00 2001 From: Alex <1010788+bulbazord@users.noreply.github.com> Date: Mon, 14 Mar 2022 09:56:55 -0700 Subject: [PATCH 053/242] [cxx-interop] Walk initializer decls when walking constructor decls (#41584) This addresses SR-15272. When you have a function (e.g. 'foo') which is used in a constructor initializer (e.g. constructor of some class 'Bar'), and you use the constructor from swift code without directly using the function (e.g. Bar's ctor is used from swift but foo isn't, foo is used from Bar's ctor), you will get a link error. My fix is as follows: When walking the clangAST to be imported, make sure you look at each CXXCtorInitializer for each CXXConstructorDecl you see. --- lib/IRGen/GenClangDecl.cpp | 9 +++++++++ ...constructor-calls-function-from-nested-calls.h | 13 +++++++++++++ ...onstructor-calls-function-from-nested-struct.h | 14 ++++++++++++++ .../Inputs/constructor-calls-function.h | 15 +++++++++++++++ .../Inputs/module.modulemap | 5 +++++ .../constructor-calls-function-execution.swift | 12 +++++++++++- ...lls-function-from-nested-calls-execution.swift | 15 +++++++++++++++ ...r-calls-function-from-nested-calls-irgen.swift | 10 ++++++++++ ...ls-function-from-nested-struct-execution.swift | 7 ++++++- ...-calls-function-from-nested-struct-irgen.swift | 8 +++++++- .../constructor-calls-function-irgen.swift | 9 ++++++++- 11 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function-from-nested-calls.h create mode 100644 test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-calls-execution.swift create mode 100644 test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-calls-irgen.swift diff --git a/lib/IRGen/GenClangDecl.cpp b/lib/IRGen/GenClangDecl.cpp index 6c627de50048c..23d51ce14a18a 100644 --- a/lib/IRGen/GenClangDecl.cpp +++ b/lib/IRGen/GenClangDecl.cpp @@ -53,6 +53,15 @@ class ClangDeclFinder return true; } + bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *CXXCD) { + callback(CXXCD); + for (clang::CXXCtorInitializer *CXXCI : CXXCD->inits()) { + if (clang::FieldDecl *FD = CXXCI->getMember()) + callback(FD); + } + return true; + } + bool VisitCXXConstructExpr(clang::CXXConstructExpr *CXXCE) { callback(CXXCE->getConstructor()); return true; diff --git a/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function-from-nested-calls.h b/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function-from-nested-calls.h new file mode 100644 index 0000000000000..812fc27afb034 --- /dev/null +++ b/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function-from-nested-calls.h @@ -0,0 +1,13 @@ +#ifndef TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_CODEGEN_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_FROM_NESTED_CALLS_H +#define TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_CODEGEN_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_FROM_NESTED_CALLS_H + +inline int get42Level4() { return 42; } +inline int get42Level3() { return get42Level4(); } +inline int get42Level2() { return get42Level3(); } +inline int get42Level1() { return get42Level2(); } + +struct Hold42WithLongInitCallGraph { + int m = get42Level1(); +}; + +#endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_FROM_NESTED_STRUCT_H diff --git a/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function-from-nested-struct.h b/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function-from-nested-struct.h index dfe1bccada1b1..4df8bd1874044 100644 --- a/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function-from-nested-struct.h +++ b/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function-from-nested-struct.h @@ -14,4 +14,18 @@ inline int callConstructor(int value) { return IncrementUser::Incrementor(value).value; } +inline int get42() { return 42; } + +struct HoldMemberThatHolds42 { + struct Hold42 { + int m = get42(); + }; + + Hold42 holder; +}; + +struct HoldMemberThatHoldsMemberThatHolds42 { + HoldMemberThatHolds42 holder; +}; + #endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_FROM_NESTED_STRUCT_H diff --git a/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function.h b/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function.h index f2140bfb7e557..f618d7432bdce 100644 --- a/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function.h +++ b/test/Interop/Cxx/class/inline-function-codegen/Inputs/constructor-calls-function.h @@ -10,4 +10,19 @@ struct Incrementor { inline int callConstructor(int value) { return Incrementor(value).incrementee; } +inline int get42() { return 42; } + +template +T passThroughArgT(T t) { + return t; +} + +struct Hold42 { + int m = get42(); +}; + +struct Hold23 { + int m = passThroughArgT(23); +}; + #endif // TEST_INTEROP_CXX_CLASS_INLINE_FUNCTION_THROUGH_MEMBER_INPUTS_CONSTRUCTOR_CALLS_FUNCTION_H diff --git a/test/Interop/Cxx/class/inline-function-codegen/Inputs/module.modulemap b/test/Interop/Cxx/class/inline-function-codegen/Inputs/module.modulemap index 7f544f3b9e97e..498c3bb59627d 100644 --- a/test/Interop/Cxx/class/inline-function-codegen/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/inline-function-codegen/Inputs/module.modulemap @@ -8,6 +8,11 @@ module ConstructorCallsFunctionFromNestedStruct { requires cplusplus } +module ConstructorCallsFunctionFromNestedCalls { + header "constructor-calls-function-from-nested-calls.h" + requires cplusplus +} + module ConstructorCallsMethod { header "constructor-calls-method.h" requires cplusplus diff --git a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-execution.swift b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-execution.swift index 20fd2aead1317..8699070610a8c 100644 --- a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-execution.swift +++ b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-execution.swift @@ -7,8 +7,18 @@ import StdlibUnittest var MembersTestSuite = TestSuite("MembersTestSuite") -MembersTestSuite.test("constructor calls function") { +MembersTestSuite.test("constructor calls function (explicit)") { expectEqual(42, callConstructor(41)) } +MembersTestSuite.test("constructor calls function (implicit)") { + let holder = Hold42() + expectEqual(42, holder.m) +} + +MembersTestSuite.test("constructor calls template function (implicit)") { + let holder = Hold23() + expectEqual(23, holder.m) +} + runAllTests() diff --git a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-calls-execution.swift b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-calls-execution.swift new file mode 100644 index 0000000000000..f16f34305a688 --- /dev/null +++ b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-calls-execution.swift @@ -0,0 +1,15 @@ +// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop) +// +// REQUIRES: executable_test + +import ConstructorCallsFunctionFromNestedCalls +import StdlibUnittest + +var MembersTestSuite = TestSuite("MembersTestSuite") + +MembersTestSuite.test("constructor calls function from nested calls") { + let holder = Hold42WithLongInitCallGraph() + expectEqual(42, holder.m) +} + +runAllTests() diff --git a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-calls-irgen.swift b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-calls-irgen.swift new file mode 100644 index 0000000000000..e40863bed0e66 --- /dev/null +++ b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-calls-irgen.swift @@ -0,0 +1,10 @@ +// RUN: %target-swift-emit-ir %s -I %S/Inputs -enable-cxx-interop | %FileCheck %s + +import ConstructorCallsFunctionFromNestedCalls + +let a = Hold42WithLongInitCallGraph() + +// CHECK-DAG: define linkonce_odr{{( dso_local)?}} i32 @{{_Z11get42Level1v|"\?get42Level1@@YAHXZ"}} +// CHECK-DAG: define linkonce_odr{{( dso_local)?}} i32 @{{_Z11get42Level2v|"\?get42Level2@@YAHXZ"}} +// CHECK-DAG: define linkonce_odr{{( dso_local)?}} i32 @{{_Z11get42Level3v|"\?get42Level3@@YAHXZ"}} +// CHECK-DAG: define linkonce_odr{{( dso_local)?}} i32 @{{_Z11get42Level4v|"\?get42Level4@@YAHXZ"}} diff --git a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-struct-execution.swift b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-struct-execution.swift index 0273128ecbd7e..464f977bee9db 100644 --- a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-struct-execution.swift +++ b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-struct-execution.swift @@ -7,8 +7,13 @@ import StdlibUnittest var MembersTestSuite = TestSuite("MembersTestSuite") -MembersTestSuite.test("constructor calls function from nested struct") { +MembersTestSuite.test("constructor calls function from nested struct (explicit)") { expectEqual(42, callConstructor(41)) } +MembersTestSuite.test("constructor calls function from nested struct (implicit)") { + let holder = HoldMemberThatHolds42() + expectEqual(42, holder.holder.m) +} + runAllTests() diff --git a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-struct-irgen.swift b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-struct-irgen.swift index cf37774179d90..d7b5cfff538e0 100644 --- a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-struct-irgen.swift +++ b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-from-nested-struct-irgen.swift @@ -6,4 +6,10 @@ public func getIncrementorValue() -> CInt { return callConstructor(41) } -// CHECK: define linkonce_odr{{( dso_local)?}} i32 @{{_Z9incrementi|"\?increment@@YAHH@Z"}}(i32 %t) +let a = HoldMemberThatHolds42() +let b = HoldMemberThatHoldsMemberThatHolds42() + +let sum = a.holder.m + b.holder.holder.m + +// CHECK-DAG: define linkonce_odr{{( dso_local)?}} i32 @{{_Z9incrementi|"\?increment@@YAHH@Z"}}(i32 %t) +// CHECK-DAG: define linkonce_odr{{( dso_local)?}} i32 @{{_Z5get42v|"\?get42@@YAHXZ"}} diff --git a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-irgen.swift b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-irgen.swift index df594af1a4399..fa60b649f1fd9 100644 --- a/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-irgen.swift +++ b/test/Interop/Cxx/class/inline-function-codegen/constructor-calls-function-irgen.swift @@ -6,4 +6,11 @@ public func getIncrementorValue() -> CInt { return callConstructor(41) } -// CHECK: define linkonce_odr{{( dso_local)?}} i32 @{{_Z9incrementi|"\?increment@@YAHH@Z"}}(i32 %t) +let a = Hold42() +let b = Hold23() + +let sum = a.m + b.m + +// CHECK-DAG: define linkonce_odr{{( dso_local)?}} i32 @{{_Z9incrementi|"\?increment@@YAHH@Z"}}(i32 %t) +// CHECK-DAG: define linkonce_odr{{( dso_local)?}} i32 @{{_Z5get42v|"\?get42@@YAHXZ"}} +// CHECK-DAG: define linkonce_odr{{( dso_local)?}} i32 @{{_Z15passThroughArgTIiET_S0_|"\?\?\$passThroughArgT@H@@YAHH@Z"}} From 894c932ad018333a93ad97ff3facad045645a5f0 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Mon, 14 Mar 2022 10:07:13 -0700 Subject: [PATCH 054/242] [CSApply] Use decl context of target when applying solution to it Solution application target can have its declaration context differ from one used for constraint system, since target could be e.g. a pattern associated with pattern binding declaration, a statement or a sub-element of one (e.g. where clause) used in a closure etc. --- lib/Sema/CSApply.cpp | 3 ++- test/expr/closure/multi_statement.swift | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 687c216796fd2..0daf5a6756ea3 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -2281,7 +2281,8 @@ namespace { ExprRewriter(ConstraintSystem &cs, Solution &solution, Optional target, bool suppressDiagnostics) - : cs(cs), dc(cs.DC), solution(solution), target(target), + : cs(cs), dc(target ? target->getDeclContext() : cs.DC), + solution(solution), target(target), SuppressDiagnostics(suppressDiagnostics) {} ConstraintSystem &getConstraintSystem() const { return cs; } diff --git a/test/expr/closure/multi_statement.swift b/test/expr/closure/multi_statement.swift index f00ccf1c284e4..efb2b1105d96c 100644 --- a/test/expr/closure/multi_statement.swift +++ b/test/expr/closure/multi_statement.swift @@ -233,3 +233,19 @@ func test_local_function_capturing_vars() { } } } + +func test_taps_type_checked_with_correct_decl_context() { + struct Path { + func contains(_: T) -> Bool where T: StringProtocol { return false } + } + + let paths: [Path] = [] + let strs: [String] = [] + + _ = paths.filter { path in + for str in strs where path.contains("\(str).hello") { + return true + } + return false + } +} From b555b6083b8ad342d7ccca84a9ba8dc6ff2f678b Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 10 Mar 2022 15:31:38 -0500 Subject: [PATCH 055/242] [Concurrency] Decode actor/task flags in signposts, make task_wait an interval, signpost continuations. Decode all fields from the various flags values, pass each field as a separate argument to the various signposts. We were just passing the raw value of the flags and requiring the signpost client to decode them, which was ugly and required the client to know details they shouldn't need to know. Strip ptrauth bits from the task resume function when signposting, when ptrauth is supported. Add signpost events for continuation init/await/resume events. We also make task_wait into an interval, rather than a single event. The interval ends when the task resumes. As part of this change, we also skip emitting the interval when the wait completed immediately and the task didn't have to suspend. While we're in there, clean up a few SWIFT_TASK_DEBUG_LOG lines that emitted warnings when built with the logging enabled. rdar://88658803 --- stdlib/public/Concurrency/Actor.cpp | 48 ++++++++++--- stdlib/public/Concurrency/Task.cpp | 33 ++++++--- stdlib/public/Concurrency/TaskPrivate.h | 9 ++- stdlib/public/Concurrency/Tracing.h | 31 +++++--- stdlib/public/Concurrency/TracingSignpost.h | 78 +++++++++++++++++---- stdlib/public/Concurrency/TracingStubs.h | 26 +++++-- 6 files changed, 177 insertions(+), 48 deletions(-) diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index fe21527e144a4..87031cb1800ed 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -817,6 +817,30 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus { return offsetof(ActiveActorStatus, DrainLock); } #endif + + void traceStateChanged(HeapObject *actor) { + // Convert our state to a consistent raw value. These values currently match + // the enum values, but this explicit conversion provides room for change. + uint8_t traceState = 255; + switch (getActorState()) { + case ActiveActorStatus::Idle: + traceState = 0; + break; + case ActiveActorStatus::Scheduled: + traceState = 1; + break; + case ActiveActorStatus::Running: + traceState = 2; + break; + case ActiveActorStatus::Zombie_ReadyForDeallocation: + traceState = 3; + break; + } + concurrency::trace::actor_state_changed( + actor, getFirstJob().getRawJob(), getFirstJob().needsPreprocessing(), + traceState, isDistributedRemote(), isMaxPriorityEscalated(), + static_cast(getMaxPriority())); + } }; #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES @@ -943,7 +967,10 @@ class DefaultActorImpl : public HeapObject { // Only for static assert use below, not for actual use otherwise static constexpr size_t offsetOfActiveActorStatus() { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winvalid-offsetof" return offsetof(DefaultActorImpl, StatusStorage); +#pragma clang diagnostic pop } private: @@ -1124,11 +1151,10 @@ static void traceJobQueue(DefaultActorImpl *actor, Job *first) { static SWIFT_ATTRIBUTE_ALWAYS_INLINE void traceActorStateTransition(DefaultActorImpl *actor, ActiveActorStatus oldState, ActiveActorStatus newState) { - SWIFT_TASK_DEBUG_LOG("Actor %p transitioned from %zx to %zx (%s)\n", actor, - oldState.getOpaqueFlags(), newState.getOpaqueFlags(), __FUNCTION__); - concurrency::trace::actor_state_changed(actor, - newState.getFirstJob().getRawJob(), newState.getFirstJob().needsPreprocessing(), - newState.getOpaqueFlags()); + SWIFT_TASK_DEBUG_LOG("Actor %p transitioned from %#x to %#x (%s)\n", actor, + oldState.getOpaqueFlags(), newState.getOpaqueFlags(), + __FUNCTION__); + newState.traceStateChanged(actor); } void DefaultActorImpl::destroy() { @@ -1192,7 +1218,9 @@ void DefaultActorImpl::scheduleActorProcessJob(JobPriority priority, bool useInl swift_retain(this); job = new ProcessOutOfLineJob(this, priority); } - SWIFT_TASK_DEBUG_LOG("Scheduling processing job %p for actor %p at priority %#x", job, this, priority); + SWIFT_TASK_DEBUG_LOG( + "Scheduling processing job %p for actor %p at priority %#zx", job, this, + priority); swift_task_enqueueGlobal(job); } @@ -1259,7 +1287,9 @@ void DefaultActorImpl::enqueue(Job *job, JobPriority priority) { // We can do relaxed loads here, we are just using the current head in the // atomic state and linking that into the new job we are inserting, we don't // need acquires - SWIFT_TASK_DEBUG_LOG("Enqueueing job %p onto actor %p at priority %#x", job, this, priority); + SWIFT_TASK_DEBUG_LOG("Enqueueing job %p onto actor %p at priority %#zx", job, + this, priority); + concurrency::trace::actor_enqueue(this, job); auto oldState = _status().load(std::memory_order_relaxed); while (true) { auto newState = oldState; @@ -1422,6 +1452,7 @@ Job * DefaultActorImpl::drainOne() { /* failure */ std::memory_order_acquire)) { SWIFT_TASK_DEBUG_LOG("Drained first job %p from actor %p", firstJob, this); traceActorStateTransition(this, oldState, newState); + concurrency::trace::actor_dequeue(this, firstJob); return firstJob; } @@ -1650,7 +1681,8 @@ static bool canGiveUpThreadForSwitch(ExecutorTrackingInfo *trackingInfo, /// do that in runOnAssumedThread. static void giveUpThreadForSwitch(ExecutorRef currentExecutor) { if (currentExecutor.isGeneric()) { - SWIFT_TASK_DEBUG_LOG("Giving up current generic executor %p", currentExecutor); + SWIFT_TASK_DEBUG_LOG("Giving up current generic executor %p", + currentExecutor.getIdentity()); return; } diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index e470481164716..1bdbea0bc1b6b 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -107,8 +107,6 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask, auto queueHead = fragment->waitQueue.load(std::memory_order_acquire); bool contextIntialized = false; while (true) { - concurrency::trace::task_wait( - waitingTask, this, static_cast(queueHead.getStatus())); switch (queueHead.getStatus()) { case Status::Error: case Status::Success: @@ -123,6 +121,8 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask, SWIFT_TASK_DEBUG_LOG("task %p waiting on task %p, going to sleep", waitingTask, this); _swift_tsan_release(static_cast(waitingTask)); + concurrency::trace::task_wait( + waitingTask, this, static_cast(queueHead.getStatus())); // Task is not complete. We'll need to add ourselves to the queue. break; } @@ -233,6 +233,8 @@ void AsyncTask::completeFuture(AsyncContext *context) { _swift_tsan_acquire(static_cast(waitingTask)); + concurrency::trace::task_resume(waitingTask); + // Enqueue the waiter on the global executor. // TODO: allow waiters to fill in a suggested executor waitingTask->flagAsAndEnqueueOnExecutor(ExecutorRef::generic()); @@ -470,23 +472,27 @@ const void reinterpret_cast(task_future_wait_resume_adapter); const void *AsyncTask::getResumeFunctionForLogging() { + const void *result = reinterpret_cast(ResumeTask); + if (ResumeTask == non_future_adapter) { auto asyncContextPrefix = reinterpret_cast( reinterpret_cast(ResumeContext) - sizeof(AsyncContextPrefix)); - return reinterpret_cast(asyncContextPrefix->asyncEntryPoint); + result = + reinterpret_cast(asyncContextPrefix->asyncEntryPoint); } else if (ResumeTask == future_adapter) { auto asyncContextPrefix = reinterpret_cast( reinterpret_cast(ResumeContext) - sizeof(FutureAsyncContextPrefix)); - return reinterpret_cast(asyncContextPrefix->asyncEntryPoint); + result = + reinterpret_cast(asyncContextPrefix->asyncEntryPoint); } else if (ResumeTask == task_wait_throwing_resume_adapter) { auto context = static_cast(ResumeContext); - return reinterpret_cast(context->ResumeParent); + result = reinterpret_cast(context->ResumeParent); } else if (ResumeTask == task_future_wait_resume_adapter) { - return reinterpret_cast(ResumeContext->ResumeParent); + result = reinterpret_cast(ResumeContext->ResumeParent); } - return reinterpret_cast(ResumeTask); + return __ptrauth_swift_runtime_function_entry_strip(result); } JobPriority swift::swift_task_currentPriority(AsyncTask *task) @@ -653,7 +659,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl( basePriority = JobPriority::Default; } - SWIFT_TASK_DEBUG_LOG("Task's base priority = %#x", basePriority); + SWIFT_TASK_DEBUG_LOG("Task's base priority = %#zx", basePriority); // Figure out the size of the header. size_t headerSize = sizeof(AsyncTask); @@ -788,7 +794,9 @@ static AsyncTaskAndContext swift_task_create_commonImpl( futureAsyncContextPrefix->indirectResult = futureFragment->getStoragePtr(); } - SWIFT_TASK_DEBUG_LOG("creating task %p with parent %p at base pri %zu", task, parent, basePriority); + SWIFT_TASK_DEBUG_LOG("creating task %p ID %" PRIu64 + " with parent %p at base pri %zu", + task, task->getTaskId(), parent, basePriority); // Initialize the task-local allocator. initialContext->ResumeParent = reinterpret_cast( @@ -1059,6 +1067,8 @@ static AsyncTask *swift_continuation_initImpl(ContinuationAsyncContext *context, task->ResumeContext = context; task->ResumeTask = context->ResumeParent; + concurrency::trace::task_continuation_init(task, context); + return task; } @@ -1071,6 +1081,8 @@ static void swift_continuation_awaitImpl(ContinuationAsyncContext *context) { assert(task->ResumeTask == context->ResumeParent); #endif + concurrency::trace::task_continuation_await(context); + auto &sync = context->AwaitSynchronization; auto oldStatus = sync.load(std::memory_order_acquire); @@ -1157,12 +1169,14 @@ static void resumeTaskAfterContinuation(AsyncTask *task, SWIFT_CC(swift) static void swift_continuation_resumeImpl(AsyncTask *task) { auto context = static_cast(task->ResumeContext); + concurrency::trace::task_continuation_resume(context, false); resumeTaskAfterContinuation(task, context); } SWIFT_CC(swift) static void swift_continuation_throwingResumeImpl(AsyncTask *task) { auto context = static_cast(task->ResumeContext); + concurrency::trace::task_continuation_resume(context, false); resumeTaskAfterContinuation(task, context); } @@ -1171,6 +1185,7 @@ SWIFT_CC(swift) static void swift_continuation_throwingResumeWithErrorImpl(AsyncTask *task, /* +1 */ SwiftError *error) { auto context = static_cast(task->ResumeContext); + concurrency::trace::task_continuation_resume(context, true); context->ErrorResult = error; resumeTaskAfterContinuation(task, context); } diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h index bed9b9d451de5..bd714fc85afbc 100644 --- a/stdlib/public/Concurrency/TaskPrivate.h +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -507,7 +507,9 @@ class alignas(2 * sizeof(void*)) ActiveTaskStatus { } void traceStatusChanged(AsyncTask *task) { - concurrency::trace::task_status_changed(task, Flags); + concurrency::trace::task_status_changed( + task, static_cast(getStoredPriority()), isCancelled(), + isStoredPriorityEscalated(), isRunning(), isEnqueued()); } }; @@ -771,7 +773,10 @@ inline void AsyncTask::flagAsAndEnqueueOnExecutor(ExecutorRef newExecutor) { // Set up task for enqueue to next location by setting the Job priority field Flags.setPriority(newStatus.getStoredPriority()); - concurrency::trace::task_flags_changed(this, Flags.getOpaqueValue()); + concurrency::trace::task_flags_changed( + this, static_cast(Flags.getPriority()), Flags.task_isChildTask(), + Flags.task_isFuture(), Flags.task_isGroupChildTask(), + Flags.task_isAsyncLetTask()); swift_task_enqueue(this, newExecutor); } diff --git a/stdlib/public/Concurrency/Tracing.h b/stdlib/public/Concurrency/Tracing.h index 7f76596751a65..ccbd9b7b8fcd3 100644 --- a/stdlib/public/Concurrency/Tracing.h +++ b/stdlib/public/Concurrency/Tracing.h @@ -22,6 +22,7 @@ namespace swift { class AsyncLet; class AsyncTask; +class ContinuationAsyncContext; class ExecutorRef; struct HeapObject; class Job; @@ -43,10 +44,13 @@ void actor_enqueue(HeapObject *actor, Job *job); void actor_dequeue(HeapObject *actor, Job *job); -// The `flags` parameter is the raw values of the actor's -// DefaultActorImpl::State::Flags. +// State values are: +// Idle = 0, Scheduled = 1, Running = 2, Zombie_ReadyForDeallocation = 3, +// invalid/unknown = 255 void actor_state_changed(HeapObject *actor, Job *firstJob, - bool needsPreprocessing, uintptr_t flags); + bool needsPreprocessing, uint8_t state, + bool isDistributedRemote, bool isPriorityEscalated, + uint8_t maxPriority); void actor_note_job_queue(HeapObject *actor, Job *first, Job *(*getNext)(Job *)); @@ -58,17 +62,28 @@ void task_create(AsyncTask *task, AsyncTask *parent, TaskGroup *group, void task_destroy(AsyncTask *task); -// The `flags` parameter is the raw value of the ActiveTaskStatus::Flags field -// in the task. -void task_status_changed(AsyncTask *task, uintptr_t flags); +void task_status_changed(AsyncTask *task, uint8_t maxPriority, bool isCancelled, + bool isEscalated, bool isRunning, bool isEnqueued); -// The `flags` parameter is the raw value of Job::Flags. -void task_flags_changed(AsyncTask *task, uint32_t flags); +void task_flags_changed(AsyncTask *task, uint8_t jobPriority, bool isChildTask, + bool isFuture, bool isGroupChildTask, + bool isAsyncLetTask); // The `status` parameter is the value of the corresponding // FutureFragment::Status. void task_wait(AsyncTask *task, AsyncTask *waitingOn, uintptr_t status); +void task_resume(AsyncTask *task); + +// The context parameter is the context pointer used to create the continuation. +// This same pointer will be passed to the corresponding call to +// task_continuation_await and task_continuation_resume. +void task_continuation_init(AsyncTask *task, ContinuationAsyncContext *context); + +void task_continuation_await(ContinuationAsyncContext *context); + +void task_continuation_resume(ContinuationAsyncContext *context, bool error); + void job_enqueue_global(Job *job); void job_enqueue_global_with_delay(unsigned long long delay, Job *job); diff --git a/stdlib/public/Concurrency/TracingSignpost.h b/stdlib/public/Concurrency/TracingSignpost.h index 1301bdfd34bdb..80425a6e15e32 100644 --- a/stdlib/public/Concurrency/TracingSignpost.h +++ b/stdlib/public/Concurrency/TracingSignpost.h @@ -55,6 +55,9 @@ #define SWIFT_LOG_TASK_FLAGS_CHANGED_NAME "task_flags_changed" #define SWIFT_LOG_TASK_STATUS_CHANGED_NAME "task_status_changed" #define SWIFT_LOG_TASK_WAIT_NAME "task_wait" +#define SWIFT_LOG_TASK_CONTINUATION_INIT "task_continuation_init" +#define SWIFT_LOG_TASK_CONTINUATION_AWAIT "task_continuation_await" +#define SWIFT_LOG_TASK_CONTINUATION_RESUME "task_continuation_resume" #define SWIFT_LOG_JOB_ENQUEUE_GLOBAL_NAME "job_enqueue_global" #define SWIFT_LOG_JOB_ENQUEUE_GLOBAL_WITH_DELAY_NAME \ "job_enqueue_global_with_delay" @@ -138,13 +141,17 @@ inline void actor_dequeue(HeapObject *actor, Job *job) { } inline void actor_state_changed(HeapObject *actor, Job *firstJob, - bool needsPreprocessing, uintptr_t flags) { + bool needsPreprocessing, uint8_t state, + bool isDistributedRemote, + bool isPriorityEscalated, uint8_t maxPriority) { ENSURE_LOGS(); auto id = os_signpost_id_make_with_pointer(ActorLog, actor); os_signpost_event_emit(ActorLog, id, SWIFT_LOG_ACTOR_STATE_CHANGED_NAME, "actor=%p needsPreprocessing=%d " - "flags=0x%" PRIxPTR, - actor, needsPreprocessing, flags); + "state=%u isDistributedRemote=%{bool}d " + "isPriorityEscalated=%{bool}d, maxPriority=%u", + actor, needsPreprocessing, state, isDistributedRemote, + isPriorityEscalated, maxPriority); } inline void actor_note_job_queue(HeapObject *actor, Job *first, @@ -190,30 +197,71 @@ inline void task_destroy(AsyncTask *task) { "task=%" PRIx64 "", task->getTaskId()); } -inline void task_status_changed(AsyncTask *task, uintptr_t flags) { +inline void task_status_changed(AsyncTask *task, uint8_t maxPriority, + bool isCancelled, bool isEscalated, + bool isRunning, bool isEnqueued) { ENSURE_LOGS(); auto id = os_signpost_id_make_with_pointer(TaskLog, task); - os_signpost_event_emit(TaskLog, id, SWIFT_LOG_TASK_STATUS_CHANGED_NAME, - "task=%" PRIx64 " resumefn=%p flags=0x%" PRIxPTR, - task->getTaskId(), task->getResumeFunctionForLogging(), - flags); + os_signpost_event_emit( + TaskLog, id, SWIFT_LOG_TASK_STATUS_CHANGED_NAME, + "task=%" PRIx64 " resumefn=%p " + "maxPriority=%u, isCancelled=%{bool}d " + "isEscalated=%{bool}d, isRunning=%{bool}d, isEnqueued=%{bool}d", + task->getTaskId(), task->getResumeFunctionForLogging(), maxPriority, + isCancelled, isEscalated, isRunning, isEnqueued); } -inline void task_flags_changed(AsyncTask *task, uint32_t flags) { +inline void task_flags_changed(AsyncTask *task, uint8_t jobPriority, + bool isChildTask, bool isFuture, + bool isGroupChildTask, bool isAsyncLetTask) { ENSURE_LOGS(); auto id = os_signpost_id_make_with_pointer(TaskLog, task); - os_signpost_event_emit(TaskLog, id, SWIFT_LOG_TASK_FLAGS_CHANGED_NAME, - "task=%" PRIx64 " flags=0x%" PRIx32, task->getTaskId(), - flags); + os_signpost_event_emit( + TaskLog, id, SWIFT_LOG_TASK_FLAGS_CHANGED_NAME, + "task=%" PRIx64 " jobPriority=%u isChildTask=%{bool}d, isFuture=%{bool}d " + "isGroupChildTask=%{bool}d isAsyncLetTask=%{bool}d", + task->getTaskId(), jobPriority, isChildTask, isFuture, isGroupChildTask, + isAsyncLetTask); } inline void task_wait(AsyncTask *task, AsyncTask *waitingOn, uintptr_t status) { ENSURE_LOGS(); auto id = os_signpost_id_make_with_pointer(TaskLog, task); auto waitingID = waitingOn ? waitingOn->getTaskId() : 0; - os_signpost_event_emit(TaskLog, id, SWIFT_LOG_TASK_WAIT_NAME, - "task=%" PRIx64 " waitingOnTask=%" PRIx64 " status=0x%" PRIxPTR, - task->getTaskId(), waitingID, status); + os_signpost_interval_begin(TaskLog, id, SWIFT_LOG_TASK_WAIT_NAME, + "task=%" PRIx64 " waitingOnTask=%" PRIx64 + " status=0x%" PRIxPTR, + task->getTaskId(), waitingID, status); +} + +inline void task_resume(AsyncTask *task) { + auto id = os_signpost_id_make_with_pointer(TaskLog, task); + os_signpost_interval_end(TaskLog, id, SWIFT_LOG_TASK_WAIT_NAME, + "task=%" PRIx64, task->getTaskId()); +} + +inline void task_continuation_init(AsyncTask *task, + ContinuationAsyncContext *context) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, context); + os_signpost_event_emit(TaskLog, id, SWIFT_LOG_TASK_CONTINUATION_INIT, + "task=%" PRIx64 " context=%p", task->getTaskId(), + context); +} + +inline void task_continuation_await(ContinuationAsyncContext *context) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, context); + os_signpost_event_emit(TaskLog, id, SWIFT_LOG_TASK_CONTINUATION_AWAIT, + "context=%p", context); +} + +inline void task_continuation_resume(ContinuationAsyncContext *context, + bool error) { + ENSURE_LOGS(); + auto id = os_signpost_id_make_with_pointer(TaskLog, context); + os_signpost_event_emit(TaskLog, id, SWIFT_LOG_TASK_CONTINUATION_RESUME, + "context=%p error=%{bool}d", context, error); } inline void job_enqueue_global(Job *job) { diff --git a/stdlib/public/Concurrency/TracingStubs.h b/stdlib/public/Concurrency/TracingStubs.h index bcdb8832cb4e4..30ec589ca97af 100644 --- a/stdlib/public/Concurrency/TracingStubs.h +++ b/stdlib/public/Concurrency/TracingStubs.h @@ -34,7 +34,10 @@ inline void actor_enqueue(HeapObject *actor, Job *job) {} inline void actor_dequeue(HeapObject *actor, Job *job) {} inline void actor_state_changed(HeapObject *actor, Job *firstJob, - bool needsPreprocessing, uintptr_t flags) {} + bool needsPreprocessing, uint8_t state, + bool isDistributedRemote, + bool isPriorityEscalated, uint8_t maxPriority) { +} inline void actor_note_job_queue(HeapObject *actor, Job *first, Job *(*getNext)(Job *)) {} @@ -44,15 +47,26 @@ inline void task_create(AsyncTask *task, AsyncTask *parent, TaskGroup *group, inline void task_destroy(AsyncTask *task) {} -inline void task_status_changed(AsyncTask *task, TaskStatusRecord *record, - uintptr_t flags) {} - inline void task_wait(AsyncTask *task, AsyncTask *waitingOn, uintptr_t status) { } -inline void task_status_changed(AsyncTask *task, uintptr_t flags) {} +inline void task_resume(AsyncTask *task) {} + +inline void task_status_changed(AsyncTask *task, uint8_t maxPriority, + bool isCancelled, bool isEscalated, + bool isRunning, bool isEnqueued) {} + +inline void task_flags_changed(AsyncTask *task, uint8_t jobPriority, + bool isChildTask, bool isFuture, + bool isGroupChildTask, bool isAsyncLetTask) {} + +inline void task_continuation_init(AsyncTask *task, + ContinuationAsyncContext *context) {} + +inline void task_continuation_await(ContinuationAsyncContext *context) {} -inline void task_flags_changed(AsyncTask *task, uint32_t flags) {} +inline void task_continuation_resume(ContinuationAsyncContext *context, + bool error) {} inline void job_enqueue_global(Job *job) {} From 5fd87a8ee39bd3976cf591810c938f7948b39db9 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 3 Mar 2022 16:01:33 -0500 Subject: [PATCH 056/242] [swift-inspect][RemoteMirror] Decode job/task/actor flags. Have RemoteMirror internally decode these flags fields and return them as separate fields in the task/actor info. Handle the structures both with and without task escalation support. Also show when a task is the current task on a thread in swift-inspect's task listing. rdar://88598003 --- include/swift/Reflection/ReflectionContext.h | 172 +++++++++++++----- include/swift/Reflection/RuntimeInternals.h | 14 +- .../SwiftRemoteMirrorTypes.h | 17 +- stdlib/public/Concurrency/TaskPrivate.h | 4 + .../SwiftRemoteMirror/SwiftRemoteMirror.cpp | 18 +- .../Operations/DumpConcurrency.swift | 77 +++++--- 6 files changed, 230 insertions(+), 72 deletions(-) diff --git a/include/swift/Reflection/ReflectionContext.h b/include/swift/Reflection/ReflectionContext.h index ea3ffcc0e00f4..da0e4c866ace7 100644 --- a/include/swift/Reflection/ReflectionContext.h +++ b/include/swift/Reflection/ReflectionContext.h @@ -43,6 +43,29 @@ #include +// The Swift runtime can be built in two ways: with or without +// SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION enabled. In order to decode the +// lock used in a runtime with priority escalation enabled, we need inline +// functions from dispatch/swift_concurrency_private.h. If we don't have that +// header at build time, we can still build but we'll be unable to decode the +// lock and thus information about a running task is degraded. There are four +// combinations: +// +// Runtime | swift_concurrency_private.h | task running info +// --------------------+-----------------------------+------------------ +// without escalation | present | full +// without escalation | not present | full +// with escalation | present | full +// with escalation | not present | DEGRADED +// +// Currently, degraded info means that IsRunning is not available (indicated +// with `HasIsRunning = false`) and async backtraces are not provided. + +#if __has_include() +#include +#define HAS_DISPATCH_LOCK_IS_LOCKED 1 +#endif + namespace { template struct MachOTraits; @@ -145,8 +168,23 @@ class ReflectionContext }; struct AsyncTaskInfo { - uint32_t JobFlags; - uint64_t TaskStatusFlags; + // Job flags. + unsigned Kind; + unsigned EnqueuePriority; + bool IsChildTask; + bool IsFuture; + bool IsGroupChildTask; + bool IsAsyncLetTask; + + // Task flags. + unsigned MaxPriority; + bool IsCancelled; + bool IsStatusRecordLocked; + bool IsEscalated; + bool HasIsRunning; // If false, the IsRunning flag is not valid. + bool IsRunning; + bool IsEnqueued; + uint64_t Id; StoredPointer RunJob; StoredPointer AllocatorSlabPtr; @@ -1435,18 +1473,91 @@ class ReflectionContext asyncTaskInfo(StoredPointer AsyncTaskPtr) { loadTargetPointers(); - if (supportsPriorityEscalation) { - return {std::string("Failure reading async task with escalation support"), {}}; - } + if (supportsPriorityEscalation) + return asyncTaskInfo< + AsyncTask>>( + AsyncTaskPtr); + else + return asyncTaskInfo< + AsyncTask>>( + AsyncTaskPtr); + } + + std::pair, ActorInfo> + actorInfo(StoredPointer ActorPtr) { + if (supportsPriorityEscalation) + return actorInfo< + DefaultActorImpl>>( + ActorPtr); + else + return actorInfo>>(ActorPtr); + } + + StoredPointer nextJob(StoredPointer JobPtr) { + using Job = Job; + + auto JobBytes = getReader().readBytes(RemoteAddress(JobPtr), sizeof(Job)); + auto *JobObj = reinterpret_cast(JobBytes.get()); + if (!JobObj) + return 0; + + // This is a JobRef which stores flags in the low bits. + return JobObj->SchedulerPrivate[0] & ~StoredPointer(0x3); + } + +private: + void setIsRunning( + AsyncTaskInfo &Info, + const AsyncTask> *Task) { +#if HAS_DISPATCH_LOCK_IS_LOCKED + Info.HasIsRunning = true; + Info.IsRunning = + dispatch_lock_is_locked(Task->PrivateStorage.Status.ExecutionLock[0]); +#else + // The target runtime was built with priority escalation but we don't have + // the swift_concurrency_private.h header needed to decode the running + // status in the task. Set HasIsRunning to false to indicate that we can't + // tell whether or not the task is running. + Info.HasIsRunning = false; +#endif + } + + void setIsRunning( + AsyncTaskInfo &Info, + const AsyncTask> + *Task) { + Info.HasIsRunning = true; + Info.IsRunning = + Task->PrivateStorage.Status.Flags[0] & ActiveTaskStatusFlags::IsRunning; + } - using AsyncTask = AsyncTask>; - auto AsyncTaskObj = readObj(AsyncTaskPtr); + template + std::pair, AsyncTaskInfo> + asyncTaskInfo(StoredPointer AsyncTaskPtr) { + auto AsyncTaskObj = readObj(AsyncTaskPtr); if (!AsyncTaskObj) return {std::string("failure reading async task"), {}}; AsyncTaskInfo Info{}; - Info.JobFlags = AsyncTaskObj->Flags; - Info.TaskStatusFlags = AsyncTaskObj->PrivateStorage.Status.Flags[0]; + + swift::JobFlags JobFlags(AsyncTaskObj->Flags); + Info.Kind = static_cast(JobFlags.getKind()); + Info.EnqueuePriority = static_cast(JobFlags.getPriority()); + Info.IsChildTask = JobFlags.task_isChildTask(); + Info.IsFuture = JobFlags.task_isFuture(); + Info.IsGroupChildTask = JobFlags.task_isGroupChildTask(); + Info.IsAsyncLetTask = JobFlags.task_isAsyncLetTask(); + + uint32_t TaskStatusFlags = AsyncTaskObj->PrivateStorage.Status.Flags[0]; + Info.IsCancelled = TaskStatusFlags & ActiveTaskStatusFlags::IsCancelled; + Info.IsStatusRecordLocked = + TaskStatusFlags & ActiveTaskStatusFlags::IsStatusRecordLocked; + Info.IsEscalated = TaskStatusFlags & ActiveTaskStatusFlags::IsEscalated; + Info.IsEnqueued = TaskStatusFlags & ActiveTaskStatusFlags::IsEnqueued; + + setIsRunning(Info, AsyncTaskObj.get()); + Info.Id = AsyncTaskObj->Id | ((uint64_t)AsyncTaskObj->PrivateStorage.Id << 32); Info.AllocatorSlabPtr = AsyncTaskObj->PrivateStorage.Allocator.FirstSlab; @@ -1479,8 +1590,7 @@ class ReflectionContext while (ChildTask) { Info.ChildTasks.push_back(ChildTask); - StoredPointer ChildFragmentAddr = - ChildTask + sizeof(AsyncTask); + StoredPointer ChildFragmentAddr = ChildTask + sizeof(*AsyncTaskObj); auto ChildFragmentObj = readObj>(ChildFragmentAddr); if (ChildFragmentObj) @@ -1492,13 +1602,8 @@ class ReflectionContext RecordPtr = RecordObj->Parent; } - // Walk the async backtrace if the task isn't running or cancelled. - // TODO: Use isEnqueued from https://github.com/apple/swift/pull/41088/ once - // that's available. - int IsCancelledFlag = 0x100; - int IsRunningFlag = 0x800; - if (!(AsyncTaskObj->PrivateStorage.Status.Flags[0] & IsCancelledFlag) && - !(AsyncTaskObj->PrivateStorage.Status.Flags[0] & IsRunningFlag)) { + // Walk the async backtrace. + if (Info.HasIsRunning && !Info.IsRunning) { auto ResumeContext = AsyncTaskObj->ResumeContextAndReserved[0]; while (ResumeContext) { auto ResumeContextObj = readObj>(ResumeContext); @@ -1513,15 +1618,10 @@ class ReflectionContext return {llvm::None, Info}; } + template std::pair, ActorInfo> actorInfo(StoredPointer ActorPtr) { - if (supportsPriorityEscalation) { - return {std::string("Failure reading actor with escalation support"), {}}; - } - - using DefaultActorImpl = DefaultActorImpl>; - - auto ActorObj = readObj(ActorPtr); + auto ActorObj = readObj(ActorPtr); if (!ActorObj) return {std::string("failure reading actor"), {}}; @@ -1538,22 +1638,10 @@ class ReflectionContext return {llvm::None, Info}; } - StoredPointer nextJob(StoredPointer JobPtr) { - using Job = Job; - - auto JobBytes = getReader().readBytes(RemoteAddress(JobPtr), sizeof(Job)); - auto *JobObj = reinterpret_cast(JobBytes.get()); - if (!JobObj) - return 0; - - // This is a JobRef which stores flags in the low bits. - return JobObj->SchedulerPrivate[0] & ~StoredPointer(0x3); - } - -private: // Get the most human meaningful "run job" function pointer from the task, // like AsyncTask::getResumeFunctionForLogging does. - StoredPointer getRunJob(const AsyncTask> *AsyncTaskObj) { + template + StoredPointer getRunJob(const AsyncTaskType *AsyncTaskObj) { auto Fptr = stripSignedPointer(AsyncTaskObj->RunJob); loadTargetPointers(); @@ -1612,9 +1700,11 @@ class ReflectionContext getFunc("_swift_concurrency_debug_task_wait_throwing_resume_adapter"); target_task_future_wait_resume_adapter = getFunc("_swift_concurrency_debug_task_future_wait_resume_adapter"); - auto supportsPriorityEscalationAddr = getReader().getSymbolAddress("_swift_concurrency_debug_supportsPriorityEscalation"); + auto supportsPriorityEscalationAddr = getReader().getSymbolAddress( + "_swift_concurrency_debug_supportsPriorityEscalation"); if (supportsPriorityEscalationAddr) { - getReader().readInteger(supportsPriorityEscalationAddr, &supportsPriorityEscalation); + getReader().readInteger(supportsPriorityEscalationAddr, + &supportsPriorityEscalation); } setupTargetPointers = true; diff --git a/include/swift/Reflection/RuntimeInternals.h b/include/swift/Reflection/RuntimeInternals.h index be8b1ad1d3edd..6941f1546750c 100644 --- a/include/swift/Reflection/RuntimeInternals.h +++ b/include/swift/Reflection/RuntimeInternals.h @@ -95,7 +95,7 @@ struct StackAllocator { template struct ActiveTaskStatusWithEscalation { - uint32_t Flags; + uint32_t Flags[1]; uint32_t ExecutionLock[(sizeof(typename Runtime::StoredPointer) == 8) ? 1 : 2]; typename Runtime::StoredPointer Record; }; @@ -106,6 +106,16 @@ struct ActiveTaskStatusWithoutEscalation { typename Runtime::StoredPointer Record; }; +struct ActiveTaskStatusFlags { + static const uint32_t PriorityMask = 0xFF; + static const uint32_t IsCancelled = 0x100; + static const uint32_t IsStatusRecordLocked = 0x200; + static const uint32_t IsEscalated = 0x400; + static const uint32_t IsRunning = 0x800; + static const uint32_t IsEnqueued = 0x1000; + static const uint32_t IsComplete = 0x2000; +}; + template struct AsyncTaskPrivateStorage { typename Runtime::StoredPointer ExclusivityAccessSet[2]; @@ -150,7 +160,7 @@ struct FutureAsyncContextPrefix { template struct ActiveActorStatusWithEscalation { - uint32_t Flags; + uint32_t Flags[1]; uint32_t DrainLock[(sizeof(typename Runtime::StoredPointer) == 8) ? 1 : 2]; typename Runtime::StoredPointer FirstJob; }; diff --git a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h index 7b7b2955fb948..04b3165a1bd2e 100644 --- a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h +++ b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h @@ -233,8 +233,21 @@ typedef struct swift_async_task_info { /// swift_reflection call on the given context. const char *Error; - uint32_t JobFlags; - uint64_t TaskStatusFlags; + unsigned Kind; + unsigned EnqueuePriority; + uint8_t IsChildTask; + uint8_t IsFuture; + uint8_t IsGroupChildTask; + uint8_t IsAsyncLetTask; + + unsigned MaxPriority; + uint8_t IsCancelled; + uint8_t IsStatusRecordLocked; + uint8_t IsEscalated; + uint8_t HasIsRunning; // If false, the IsRunning flag is not valid. + uint8_t IsRunning; + uint8_t IsEnqueued; + uint64_t Id; swift_reflection_ptr_t RunJob; swift_reflection_ptr_t AllocatorSlabPtr; diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h index 39eb2b2b4cd3c..9572cbb74bc78 100644 --- a/stdlib/public/Concurrency/TaskPrivate.h +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -295,6 +295,10 @@ class alignas(2 * sizeof(void*)) ActiveTaskStatus { #endif }; + // Note: this structure is mirrored by ActiveTaskStatusWithEscalation and + // ActiveTaskStatusWithoutEscalation in + // include/swift/Reflection/RuntimeInternals.h. Any changes to the layout here + // must also be made there. #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES uint32_t Flags; dispatch_lock_t ExecutionLock; diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 2c6eeb7facd9d..7a0d548819076 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -875,9 +875,23 @@ swift_reflection_asyncTaskInfo(SwiftReflectionContextRef ContextRef, Result.Error = returnableCString(ContextRef, Error); return Result; } - Result.JobFlags = TaskInfo.JobFlags; - Result.TaskStatusFlags = TaskInfo.TaskStatusFlags; + + Result.Kind = TaskInfo.Kind; + Result.EnqueuePriority = TaskInfo.EnqueuePriority; + Result.IsChildTask = TaskInfo.IsChildTask; + Result.IsFuture = TaskInfo.IsFuture; + Result.IsGroupChildTask = TaskInfo.IsGroupChildTask; + Result.IsAsyncLetTask = TaskInfo.IsAsyncLetTask; + + Result.MaxPriority = TaskInfo.MaxPriority; + Result.IsCancelled = TaskInfo.IsCancelled; + Result.IsStatusRecordLocked = TaskInfo.IsStatusRecordLocked; + Result.IsEscalated = TaskInfo.IsEscalated; + Result.HasIsRunning = TaskInfo.HasIsRunning; + Result.IsRunning = TaskInfo.IsRunning; + Result.IsEnqueued = TaskInfo.IsEnqueued; Result.Id = TaskInfo.Id; + Result.RunJob = TaskInfo.RunJob; Result.AllocatorSlabPtr = TaskInfo.AllocatorSlabPtr; diff --git a/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift b/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift index c3927017e036d..af1aeb0897cf3 100644 --- a/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift +++ b/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift @@ -41,8 +41,19 @@ fileprivate class ConcurrencyDumper { struct TaskInfo { var address: swift_reflection_ptr_t - var jobFlags: UInt32 - var taskStatusFlags: UInt64 + var kind: UInt32 + var enqueuePriority: UInt32 + var isChildTask: Bool + var isFuture: Bool + var isGroupChildTask: Bool + var isAsyncLetTask: Bool + var maxPriority: UInt32 + var isCancelled: Bool + var isStatusRecordLocked: Bool + var isEscalated: Bool + var hasIsRunning: Bool + var isRunning: Bool + var isEnqueued: Bool var id: UInt64 var runJob: swift_reflection_ptr_t var allocatorSlabPtr: swift_reflection_ptr_t @@ -185,8 +196,19 @@ fileprivate class ConcurrencyDumper { return TaskInfo( address: task, - jobFlags: reflectionInfo.JobFlags, - taskStatusFlags: reflectionInfo.TaskStatusFlags, + kind: reflectionInfo.Kind, + enqueuePriority: reflectionInfo.EnqueuePriority, + isChildTask: reflectionInfo.IsChildTask != 0, + isFuture: reflectionInfo.IsFuture != 0, + isGroupChildTask: reflectionInfo.IsGroupChildTask != 0, + isAsyncLetTask: reflectionInfo.IsAsyncLetTask != 0, + maxPriority: reflectionInfo.MaxPriority, + isCancelled: reflectionInfo.IsCancelled != 0, + isStatusRecordLocked: reflectionInfo.IsStatusRecordLocked != 0, + isEscalated: reflectionInfo.IsEscalated != 0, + hasIsRunning: reflectionInfo.HasIsRunning != 0, + isRunning: reflectionInfo.IsRunning != 0, + isEnqueued: reflectionInfo.IsEnqueued != 0, id: reflectionInfo.Id, runJob: reflectionInfo.RunJob, allocatorSlabPtr: reflectionInfo.AllocatorSlabPtr, @@ -251,26 +273,20 @@ fileprivate class ConcurrencyDumper { return flagsStr } - func decodeTaskFlags(_ info: TaskInfo) -> ( - priority: UInt32, - flags: String - ) { - let priority = (info.jobFlags >> 8) & 0xff - let jobFlags = flagsStrings(flags: info.jobFlags, strings: [ - 1 << 24: "childTask", - 1 << 25: "future", - 1 << 26: "groupChildTask", - 1 << 28: "asyncLetTask" - ]) - let taskFlags = flagsStrings(flags: info.taskStatusFlags, strings: [ - 0x100: "cancelled", - 0x200: "locked", - 0x400: "escalated", - 0x800: "running" - ]) - let allFlags = jobFlags + taskFlags - let flagsStr = allFlags.isEmpty ? "0" : allFlags.joined(separator: "|") - return (priority, flagsStr) + func decodeTaskFlags(_ info: TaskInfo) -> String { + var flags: [String] = [] + if info.isChildTask { flags.append("childTask") } + if info.isFuture { flags.append("future") } + if info.isGroupChildTask { flags.append("groupChildTask") } + if info.isAsyncLetTask { flags.append("asyncLetTask") } + if info.isCancelled { flags.append("cancelled") } + if info.isStatusRecordLocked { flags.append("statusRecordLocked") } + if info.isEscalated { flags.append("escalated") } + if info.hasIsRunning && info.isRunning { flags.append("running") } + if info.isEnqueued { flags.append("enqueued") } + + let flagsStr = flags.isEmpty ? "0" : flags.joined(separator: "|") + return flagsStr } func decodeActorFlags(_ flags: UInt64) -> ( @@ -302,6 +318,14 @@ fileprivate class ConcurrencyDumper { func dumpTasks() { print("TASKS") + let missingIsRunning = tasks.contains(where: { !$1.hasIsRunning }) + if missingIsRunning { + print("warning: unable to decode is-running state of target tasks, running state and async backtraces will not be printed") + } + + let taskToThread: [swift_addr_t: swift_reflection_ptr_t] = + Dictionary(threadCurrentTasks.map{ ($1, $0) }, uniquingKeysWith: { $1 }) + var lastChilds: [Bool] = [] let hierarchy = taskHierarchy() @@ -339,7 +363,10 @@ fileprivate class ConcurrencyDumper { let flags = decodeTaskFlags(task) - output("Task \(hex: task.id) - flags=\(flags.flags) priority=\(hex: flags.priority) address=\(hex: task.address)") + output("Task \(hex: task.id) - flags=\(flags) enqueuePriority=\(hex: task.enqueuePriority) maxPriority=\(hex: task.maxPriority) address=\(hex: task.address)") + if let thread = taskToThread[task.address] { + output("current task on thread \(hex: thread)") + } if let parent = task.parent { output("parent: \(hex: parent)") } From 5f5cc9f00bd5728e2b98cefaef661d77f24a1b63 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Mon, 14 Mar 2022 15:21:46 -0700 Subject: [PATCH 057/242] Sema: Diagnose use of availability macros in conditional statements in @_backDeploy functions. Add a test verifying that expected diagnostics are emitted when referencing non-public declarations from a `@_backDeploy` function body. Resolves rdar://90270100 --- include/swift/AST/DeclContext.h | 3 +++ include/swift/AST/DiagnosticsSema.def | 11 +++++---- lib/Sema/ResilienceDiagnostics.cpp | 15 +++++------- lib/Sema/TypeCheckDeclPrimary.cpp | 2 +- lib/Sema/TypeCheckStmt.cpp | 9 +++---- test/Sema/availability_define.swift | 35 ++++++++++++++++++--------- test/attr/attr_backDeploy.swift | 22 +++++++++++++++++ 7 files changed, 65 insertions(+), 32 deletions(-) diff --git a/include/swift/AST/DeclContext.h b/include/swift/AST/DeclContext.h index b5e9de87b88d4..0be9ed087e530 100644 --- a/include/swift/AST/DeclContext.h +++ b/include/swift/AST/DeclContext.h @@ -212,6 +212,9 @@ struct FragileFunctionKind { return (lhs.kind == rhs.kind && lhs.allowUsableFromInline == rhs.allowUsableFromInline); } + + /// Casts to `unsigned` for diagnostic %selects. + unsigned getSelector() { return static_cast(kind); } }; /// A DeclContext is an AST object which acts as a semantic container diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index add4db4bbc444..d824ec647cdbb 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -5631,10 +5631,6 @@ WARNING(public_decl_needs_availability, none, "public declarations should have an availability attribute when building " "with -require-explicit-availability", ()) -ERROR(availability_macro_in_inlinable, none, - "availability macro cannot be used in inlinable %0", - (DescriptiveDeclKind)) - ERROR(attr_requires_decl_availability_for_platform,none, "'%0' requires that %1 have explicit availability for %2", (DeclAttribute, DeclName, StringRef)) @@ -5731,7 +5727,8 @@ ERROR(usable_from_inline_attr_in_protocol,none, "an '@inlinable' function|" \ "an '@_alwaysEmitIntoClient' function|" \ "a default argument value|" \ - "a property initializer in a '@frozen' type}" + "a property initializer in a '@frozen' type|" \ + "a '@_backDeploy' function'}" #define DECL_OR_ACCESSOR "%select{%0|%0 for}" @@ -5756,6 +5753,10 @@ ERROR(inlinable_decl_ref_from_hidden_module, "it is SPI}4", (DescriptiveDeclKind, DeclName, unsigned, Identifier, unsigned)) +ERROR(availability_macro_in_inlinable, none, + "availability macro cannot be used in " FRAGILE_FUNC_KIND "0", + (unsigned)) + #undef FRAGILE_FUNC_KIND NOTE(resilience_decl_declared_here_public, diff --git a/lib/Sema/ResilienceDiagnostics.cpp b/lib/Sema/ResilienceDiagnostics.cpp index b44c699ecab7d..8745b2f1d47ba 100644 --- a/lib/Sema/ResilienceDiagnostics.cpp +++ b/lib/Sema/ResilienceDiagnostics.cpp @@ -10,7 +10,8 @@ // //===----------------------------------------------------------------------===// // -// This file implements diagnostics for @inlinable. +// This file implements diagnostics for fragile functions, like those with +// @inlinable, @_alwaysEmitIntoClient, or @_backDeploy. // //===----------------------------------------------------------------------===// @@ -91,12 +92,9 @@ bool TypeChecker::diagnoseInlinableDeclRefAccess(SourceLoc loc, if (downgradeToWarning == DowngradeToWarning::Yes) diagID = diag::resilience_decl_unavailable_warn; - Context.Diags.diagnose( - loc, diagID, - D->getDescriptiveKind(), diagName, - D->getFormalAccessScope().accessLevelForDiagnostics(), - static_cast(fragileKind.kind), - isAccessor); + Context.Diags.diagnose(loc, diagID, D->getDescriptiveKind(), diagName, + D->getFormalAccessScope().accessLevelForDiagnostics(), + fragileKind.getSelector(), isAccessor); if (fragileKind.allowUsableFromInline) { Context.Diags.diagnose(D, diag::resilience_decl_declared_here, @@ -150,8 +148,7 @@ TypeChecker::diagnoseDeclRefExportability(SourceLoc loc, } else { ctx.Diags.diagnose(loc, diag::inlinable_decl_ref_from_hidden_module, D->getDescriptiveKind(), D->getName(), - static_cast(fragileKind.kind), - definingModule->getName(), + fragileKind.getSelector(), definingModule->getName(), static_cast(originKind)); } return true; diff --git a/lib/Sema/TypeCheckDeclPrimary.cpp b/lib/Sema/TypeCheckDeclPrimary.cpp index 2eb45ab44adab..3d80fe0add0f0 100644 --- a/lib/Sema/TypeCheckDeclPrimary.cpp +++ b/lib/Sema/TypeCheckDeclPrimary.cpp @@ -2268,7 +2268,7 @@ class DeclChecker : public DeclVisitor { auto kind = DC->getFragileFunctionKind(); if (kind.kind != FragileFunctionKind::None) { NTD->diagnose(diag::local_type_in_inlinable_function, NTD->getName(), - static_cast(kind.kind)); + kind.getSelector()); } // We don't support protocols outside the top level of a file. diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 08336a77ecfe0..7796632e59fbb 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -429,8 +429,8 @@ bool TypeChecker::typeCheckStmtConditionElement(StmtConditionElement &elt, // Reject inlinable code using availability macros. PoundAvailableInfo *info = elt.getAvailability(); if (auto *decl = dc->getAsDecl()) { - if (decl->getAttrs().hasAttribute() || - decl->getAttrs().hasAttribute()) + auto fragileKind = dc->getFragileFunctionKind(); + if (fragileKind.kind != FragileFunctionKind::None) for (auto queries : info->getQueries()) if (auto availSpec = dyn_cast(queries)) @@ -438,7 +438,7 @@ bool TypeChecker::typeCheckStmtConditionElement(StmtConditionElement &elt, Context.Diags.diagnose( availSpec->getMacroLoc(), swift::diag::availability_macro_in_inlinable, - decl->getDescriptiveKind()); + fragileKind.getSelector()); break; } } @@ -1754,8 +1754,7 @@ static void checkClassConstructorBody(ClassDecl *classDecl, auto kind = ctor->getFragileFunctionKind(); if (kind.kind != FragileFunctionKind::None) { ctor->diagnose(diag::class_designated_init_inlinable_resilient, - classDecl->getDeclaredInterfaceType(), - static_cast(kind.kind)); + classDecl->getDeclaredInterfaceType(), kind.getSelector()); } } diff --git a/test/Sema/availability_define.swift b/test/Sema/availability_define.swift index a8ccb163f3d6d..502db10867d7f 100644 --- a/test/Sema/availability_define.swift +++ b/test/Sema/availability_define.swift @@ -68,20 +68,31 @@ func client() { @inlinable public func forbidMacrosInInlinableCode() { - if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #available(iOS 9.0, _macOS10_11, tvOS 9.0, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #unavailable(_iOS9Aligned) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #unavailable(_iOS9, _macOS10_11) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #unavailable(iOS 9.0, _macOS10_11, tvOS 9.0) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in an '@inlinable' function}} + if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in an '@inlinable' function}} + if #available(iOS 9.0, _macOS10_11, tvOS 9.0, *) { } // expected-error {{availability macro cannot be used in an '@inlinable' function}} + if #unavailable(_iOS9Aligned) { } // expected-error {{availability macro cannot be used in an '@inlinable' function}} + if #unavailable(_iOS9, _macOS10_11) { } // expected-error {{availability macro cannot be used in an '@inlinable' function}} + if #unavailable(iOS 9.0, _macOS10_11, tvOS 9.0) { } // expected-error {{availability macro cannot be used in an '@inlinable' function}} } @_alwaysEmitIntoClient public func forbidMacrosInInlinableCode1() { - if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #available(iOS 9.0, _macOS10_11, tvOS 9.0, *) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #unavailable(_iOS9Aligned) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #unavailable(_iOS9, _macOS10_11) { } // expected-error {{availability macro cannot be used in inlinable global function}} - if #unavailable(iOS 9.0, _macOS10_11, tvOS 9.0) { } // expected-error {{availability macro cannot be used in inlinable global function}} + if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in an '@_alwaysEmitIntoClient' function}} + if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in an '@_alwaysEmitIntoClient' function}} + if #available(iOS 9.0, _macOS10_11, tvOS 9.0, *) { } // expected-error {{availability macro cannot be used in an '@_alwaysEmitIntoClient' function}} + if #unavailable(_iOS9Aligned) { } // expected-error {{availability macro cannot be used in an '@_alwaysEmitIntoClient' function}} + if #unavailable(_iOS9, _macOS10_11) { } // expected-error {{availability macro cannot be used in an '@_alwaysEmitIntoClient' function}} + if #unavailable(iOS 9.0, _macOS10_11, tvOS 9.0) { } // expected-error {{availability macro cannot be used in an '@_alwaysEmitIntoClient' function}} +} + +@available(_iOS8Aligned, *) +@_backDeploy(_iOS9Aligned) +public func forbidMacrosInInlinableCode2() { + if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in a '@_backDeploy' function}} + if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in a '@_backDeploy' function}} + if #available(iOS 9.0, _macOS10_11, tvOS 9.0, *) { } // expected-error {{availability macro cannot be used in a '@_backDeploy' function}} + if #unavailable(_iOS9Aligned) { } // expected-error {{availability macro cannot be used in a '@_backDeploy' function}} + if #unavailable(_iOS9, _macOS10_11) { } // expected-error {{availability macro cannot be used in a '@_backDeploy' function}} + if #unavailable(iOS 9.0, _macOS10_11, tvOS 9.0) { } // expected-error {{availability macro cannot be used in a '@_backDeploy' function}} } diff --git a/test/attr/attr_backDeploy.swift b/test/attr/attr_backDeploy.swift index b0b631447292a..35f80e5f44615 100644 --- a/test/attr/attr_backDeploy.swift +++ b/test/attr/attr_backDeploy.swift @@ -159,6 +159,28 @@ public struct CannotBackDeployCoroutines { } } +// MARK: - Function body diagnostics + +public struct FunctionBodyDiagnostics { + public func publicFunc() {} + @usableFromInline func usableFromInlineFunc() {} + func internalFunc() {} // expected-note {{instance method 'internalFunc()' is not '@usableFromInline' or public}} + fileprivate func fileprivateFunc() {} // expected-note {{instance method 'fileprivateFunc()' is not '@usableFromInline' or public}} + private func privateFunc() {} // expected-note {{instance method 'privateFunc()' is not '@usableFromInline' or public}} + + @available(macOS 11.0, *) + @_backDeploy(macOS 12.0) + public func backDeployedMethod() { + struct Nested {} // expected-error {{type 'Nested' cannot be nested inside a '@_backDeploy' function}} + + publicFunc() + usableFromInlineFunc() + internalFunc() // expected-error {{instance method 'internalFunc()' is internal and cannot be referenced from a '@_backDeploy' function}} + fileprivateFunc() // expected-error {{instance method 'fileprivateFunc()' is fileprivate and cannot be referenced from a '@_backDeploy' function}} + privateFunc() // expected-error {{instance method 'privateFunc()' is private and cannot be referenced from a '@_backDeploy' function}} + } +} + // MARK: - Incompatible declarations @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' may not be used on fileprivate declarations}} From 539a5b3338bca04349ba45779e21fe6588dc710d Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 14 Mar 2022 18:12:11 -0700 Subject: [PATCH 058/242] [NFC] Remove Redundant Calls to VarDecl::setIntroducer --- lib/Sema/CodeSynthesisDistributedActor.cpp | 2 -- lib/Sema/DerivedConformanceDistributedActor.cpp | 4 ---- 2 files changed, 6 deletions(-) diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 96eed482a31c6..3d825891a4c5e 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -73,8 +73,6 @@ static VarDecl *addImplicitDistributedActorIDProperty( C, StaticSpellingKind::None, propPat, /*InitExpr*/ nullptr, nominal); - propDecl->setIntroducer(VarDecl::Introducer::Let); - // mark as nonisolated, allowing access to it from everywhere propDecl->getAttrs().add( new (C) NonisolatedAttr(/*IsImplicit=*/true)); diff --git a/lib/Sema/DerivedConformanceDistributedActor.cpp b/lib/Sema/DerivedConformanceDistributedActor.cpp index 933d6eadc5e4a..607ff947758f4 100644 --- a/lib/Sema/DerivedConformanceDistributedActor.cpp +++ b/lib/Sema/DerivedConformanceDistributedActor.cpp @@ -116,8 +116,6 @@ static ValueDecl *deriveDistributedActor_id(DerivedConformance &derived) { propertyType, propertyType, /*isStatic=*/false, /*isFinal=*/true); - propDecl->setIntroducer(VarDecl::Introducer::Let); - // mark as nonisolated, allowing access to it from everywhere propDecl->getAttrs().add( new (C) NonisolatedAttr(/*IsImplicit=*/true)); @@ -147,8 +145,6 @@ static ValueDecl *deriveDistributedActor_actorSystem( propertyType, propertyType, /*isStatic=*/false, /*isFinal=*/true); - propDecl->setIntroducer(VarDecl::Introducer::Let); - // mark as nonisolated, allowing access to it from everywhere propDecl->getAttrs().add( new (C) NonisolatedAttr(/*IsImplicit=*/true)); From 2d256cb6c75de9ec96b9586736292b13c1cfbb07 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 14 Mar 2022 18:14:01 -0700 Subject: [PATCH 059/242] [NFC] Hide VarDecl::setIntroducer --- include/swift/AST/Decl.h | 11 +++++++---- lib/Sema/DerivedConformanceActor.cpp | 8 ++++---- .../DerivedConformanceAdditiveArithmetic.cpp | 3 ++- lib/Sema/DerivedConformanceCaseIterable.cpp | 6 +++--- lib/Sema/DerivedConformanceCodingKey.cpp | 6 +++--- .../DerivedConformanceDistributedActor.cpp | 6 +++--- lib/Sema/DerivedConformanceError.cpp | 1 + .../DerivedConformanceRawRepresentable.cpp | 3 ++- lib/Sema/DerivedConformances.cpp | 19 +++++++++++++++---- lib/Sema/DerivedConformances.h | 8 ++++++-- 10 files changed, 46 insertions(+), 25 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 91ae1164268bb..226b58d555c08 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5060,6 +5060,13 @@ class VarDecl : public AbstractStorageDecl { SourceLoc nameLoc, Identifier name, DeclContext *dc, StorageIsMutable_t supportsMutation); +protected: + // Only \c ParamDecl::setSpecifier is allowed to flip this - and it's also + // on the way out of that business. + void setIntroducer(Introducer value) { + Bits.VarDecl.Introducer = uint8_t(value); + } + public: VarDecl(bool isStatic, Introducer introducer, SourceLoc nameLoc, Identifier name, DeclContext *dc) @@ -5263,10 +5270,6 @@ class VarDecl : public AbstractStorageDecl { return Introducer(Bits.VarDecl.Introducer); } - void setIntroducer(Introducer value) { - Bits.VarDecl.Introducer = uint8_t(value); - } - CaptureListExpr *getParentCaptureList() const { if (!Parent) return nullptr; diff --git a/lib/Sema/DerivedConformanceActor.cpp b/lib/Sema/DerivedConformanceActor.cpp index 225865e2d893c..b43445a7475b8 100644 --- a/lib/Sema/DerivedConformanceActor.cpp +++ b/lib/Sema/DerivedConformanceActor.cpp @@ -138,10 +138,10 @@ static ValueDecl *deriveActor_unownedExecutor(DerivedConformance &derived) { } Type executorType = executorDecl->getDeclaredInterfaceType(); - auto propertyPair = - derived.declareDerivedProperty(ctx.Id_unownedExecutor, - executorType, executorType, - /*static*/ false, /*final*/ false); + auto propertyPair = derived.declareDerivedProperty( + DerivedConformance::SynthesizedIntroducer::Var, ctx.Id_unownedExecutor, + executorType, executorType, + /*static*/ false, /*final*/ false); auto property = propertyPair.first; property->setSynthesized(true); property->getAttrs().add(new (ctx) SemanticsAttr(SEMANTICS_DEFAULT_ACTOR, diff --git a/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp b/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp index 36d63ec476dda..29379d381b32a 100644 --- a/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp +++ b/lib/Sema/DerivedConformanceAdditiveArithmetic.cpp @@ -302,7 +302,8 @@ static ValueDecl *deriveAdditiveArithmetic_zero(DerivedConformance &derived) { VarDecl *propDecl; PatternBindingDecl *pbDecl; std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( - C.Id_zero, returnInterfaceTy, returnTy, /*isStatic*/ true, + DerivedConformance::SynthesizedIntroducer::Var, C.Id_zero, + returnInterfaceTy, returnTy, /*isStatic*/ true, /*isFinal*/ true); // Create property getter. diff --git a/lib/Sema/DerivedConformanceCaseIterable.cpp b/lib/Sema/DerivedConformanceCaseIterable.cpp index bc809b4e53930..2351eb005413f 100644 --- a/lib/Sema/DerivedConformanceCaseIterable.cpp +++ b/lib/Sema/DerivedConformanceCaseIterable.cpp @@ -98,9 +98,9 @@ ValueDecl *DerivedConformance::deriveCaseIterable(ValueDecl *requirement) { VarDecl *propDecl; PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) = - declareDerivedProperty(Context.Id_allCases, returnTy, returnTy, - /*isStatic=*/true, /*isFinal=*/true); + std::tie(propDecl, pbDecl) = declareDerivedProperty( + SynthesizedIntroducer::Var, Context.Id_allCases, returnTy, returnTy, + /*isStatic=*/true, /*isFinal=*/true); // Define the getter. auto *getterDecl = addGetterToReadOnlyDerivedProperty(propDecl, returnTy); diff --git a/lib/Sema/DerivedConformanceCodingKey.cpp b/lib/Sema/DerivedConformanceCodingKey.cpp index 5d0ec925fd395..73c71ed106977 100644 --- a/lib/Sema/DerivedConformanceCodingKey.cpp +++ b/lib/Sema/DerivedConformanceCodingKey.cpp @@ -160,9 +160,9 @@ static ValueDecl *deriveProperty(DerivedConformance &derived, Type type, // Define the property. VarDecl *propDecl; PatternBindingDecl *pbDecl; - std::tie(propDecl, pbDecl) = - derived.declareDerivedProperty(name, type, type, - /*isStatic=*/false, /*isFinal=*/false); + std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( + DerivedConformance::SynthesizedIntroducer::Var, name, type, type, + /*isStatic=*/false, /*isFinal=*/false); // Define the getter. auto *getterDecl = derived.addGetterToReadOnlyDerivedProperty( diff --git a/lib/Sema/DerivedConformanceDistributedActor.cpp b/lib/Sema/DerivedConformanceDistributedActor.cpp index 607ff947758f4..54457cd1e19e3 100644 --- a/lib/Sema/DerivedConformanceDistributedActor.cpp +++ b/lib/Sema/DerivedConformanceDistributedActor.cpp @@ -112,8 +112,8 @@ static ValueDecl *deriveDistributedActor_id(DerivedConformance &derived) { VarDecl *propDecl; PatternBindingDecl *pbDecl; std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( - C.Id_id, - propertyType, propertyType, + DerivedConformance::SynthesizedIntroducer::Let, C.Id_id, propertyType, + propertyType, /*isStatic=*/false, /*isFinal=*/true); // mark as nonisolated, allowing access to it from everywhere @@ -141,7 +141,7 @@ static ValueDecl *deriveDistributedActor_actorSystem( VarDecl *propDecl; PatternBindingDecl *pbDecl; std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( - C.Id_actorSystem, + DerivedConformance::SynthesizedIntroducer::Let, C.Id_actorSystem, propertyType, propertyType, /*isStatic=*/false, /*isFinal=*/true); diff --git a/lib/Sema/DerivedConformanceError.cpp b/lib/Sema/DerivedConformanceError.cpp index 94e411a0c0fa9..c7d3dc3cb5e92 100644 --- a/lib/Sema/DerivedConformanceError.cpp +++ b/lib/Sema/DerivedConformanceError.cpp @@ -97,6 +97,7 @@ deriveBridgedNSError_enum_nsErrorDomain( VarDecl *propDecl; PatternBindingDecl *pbDecl; std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( + DerivedConformance::SynthesizedIntroducer::Var, derived.Context.Id_nsErrorDomain, stringTy, stringTy, /*isStatic=*/true, /*isFinal=*/true); diff --git a/lib/Sema/DerivedConformanceRawRepresentable.cpp b/lib/Sema/DerivedConformanceRawRepresentable.cpp index 9053421e44d1d..e8317c2e1003e 100644 --- a/lib/Sema/DerivedConformanceRawRepresentable.cpp +++ b/lib/Sema/DerivedConformanceRawRepresentable.cpp @@ -165,7 +165,8 @@ static VarDecl *deriveRawRepresentable_raw(DerivedConformance &derived) { VarDecl *propDecl; PatternBindingDecl *pbDecl; std::tie(propDecl, pbDecl) = derived.declareDerivedProperty( - C.Id_rawValue, rawInterfaceType, rawType, /*isStatic=*/false, + DerivedConformance::SynthesizedIntroducer::Var, C.Id_rawValue, + rawInterfaceType, rawType, /*isStatic=*/false, /*isFinal=*/false); // Define the getter. diff --git a/lib/Sema/DerivedConformances.cpp b/lib/Sema/DerivedConformances.cpp index 09fe2bbdd0812..ccd6cf54876fc 100644 --- a/lib/Sema/DerivedConformances.cpp +++ b/lib/Sema/DerivedConformances.cpp @@ -498,16 +498,27 @@ DerivedConformance::declareDerivedPropertyGetter(VarDecl *property, return getterDecl; } +static VarDecl::Introducer +mapIntroducer(DerivedConformance::SynthesizedIntroducer intro) { + switch (intro) { + case DerivedConformance::SynthesizedIntroducer::Let: + return VarDecl::Introducer::Let; + case DerivedConformance::SynthesizedIntroducer::Var: + return VarDecl::Introducer::Var; + } + llvm_unreachable("Invalid synthesized introducer!"); +} + std::pair -DerivedConformance::declareDerivedProperty(Identifier name, +DerivedConformance::declareDerivedProperty(SynthesizedIntroducer intro, + Identifier name, Type propertyInterfaceType, Type propertyContextType, bool isStatic, bool isFinal) { auto parentDC = getConformanceContext(); - VarDecl *propDecl = new (Context) - VarDecl(/*IsStatic*/ isStatic, VarDecl::Introducer::Var, - SourceLoc(), name, parentDC); + VarDecl *propDecl = new (Context) VarDecl( + /*IsStatic*/ isStatic, mapIntroducer(intro), SourceLoc(), name, parentDC); propDecl->setImplicit(); propDecl->setSynthesized(); propDecl->copyFormalAccessFrom(Nominal, /*sourceIsParentContext*/ true); diff --git a/lib/Sema/DerivedConformances.h b/lib/Sema/DerivedConformances.h index fc508dc8ef838..383bbc84f71a0 100644 --- a/lib/Sema/DerivedConformances.h +++ b/lib/Sema/DerivedConformances.h @@ -49,6 +49,9 @@ class ValueDecl; class VarDecl; class DerivedConformance { +public: + enum class SynthesizedIntroducer : bool { Let, Var }; + public: ASTContext &Context; Decl *ConformanceDecl; @@ -335,8 +338,9 @@ class DerivedConformance { /// Declare a read-only property. std::pair - declareDerivedProperty(Identifier name, Type propertyInterfaceType, - Type propertyContextType, bool isStatic, bool isFinal); + declareDerivedProperty(SynthesizedIntroducer intro, Identifier name, + Type propertyInterfaceType, Type propertyContextType, + bool isStatic, bool isFinal); /// Add a getter to a derived property. The property becomes read-only. static AccessorDecl * From b563dc0736cd9f1392fc23518aa8ce24b070043d Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Mon, 14 Mar 2022 11:23:26 -0700 Subject: [PATCH 060/242] Frontend: Replace the `abi` magic value accepted by `-target-min-inlining-version` with a `min` magic value instead. The new value corresponds to the OS versions in which Swift was introduced. The introduction OS is a better floor for availability checking than the OS in which Swift became ABI stable because inlinable functions may reference clang declarations which have availability between Swift's introduction and ABI stability and framework developers ought to get diagnostics for unguarded use of those APIs in inlinable code. --- include/swift/Basic/Platform.h | 4 +- lib/Basic/Platform.cpp | 15 ++++--- lib/Frontend/CompilerInvocation.cpp | 17 +++----- test/attr/attr_inlinable_available.swift | 55 +++++++++++++++++++----- 4 files changed, 64 insertions(+), 27 deletions(-) diff --git a/include/swift/Basic/Platform.h b/include/swift/Basic/Platform.h index cf5450ad33868..c914c7da1d3ea 100644 --- a/include/swift/Basic/Platform.h +++ b/include/swift/Basic/Platform.h @@ -55,8 +55,10 @@ namespace swift { bool triplesAreValidForZippering(const llvm::Triple &target, const llvm::Triple &targetVariant); + /// Returns the VersionTuple at which Swift first became available for the OS + /// represented by `triple`. const Optional - minimumABIStableOSVersionForTriple(const llvm::Triple &triple); + minimumAvailableOSVersionForTriple(const llvm::Triple &triple); /// Returns true if the given triple represents an OS that has all the /// "built-in" ABI-stable libraries (stdlib and _Concurrency) diff --git a/lib/Basic/Platform.cpp b/lib/Basic/Platform.cpp index b94f4eaf7b4a1..2856d568ec2d6 100644 --- a/lib/Basic/Platform.cpp +++ b/lib/Basic/Platform.cpp @@ -80,15 +80,20 @@ bool swift::triplesAreValidForZippering(const llvm::Triple &target, } const Optional -swift::minimumABIStableOSVersionForTriple(const llvm::Triple &triple) { +swift::minimumAvailableOSVersionForTriple(const llvm::Triple &triple) { if (triple.isMacOSX()) - return llvm::VersionTuple(10, 14, 4); + return llvm::VersionTuple(10, 10, 0); - if (triple.isiOS() /* including tvOS */) - return llvm::VersionTuple(12, 2); + // Note: this must come before checking iOS since that returns true for + // both iOS and tvOS. + if (triple.isTvOS()) + return llvm::VersionTuple(9, 0); + + if (triple.isiOS()) + return llvm::VersionTuple(8, 0); if (triple.isWatchOS()) - return llvm::VersionTuple(5, 2); + return llvm::VersionTuple(2, 0); return None; } diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index bf58496dc8a44..31d0cb05dc316 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -803,15 +803,12 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, // First, set up default minimum inlining target versions. auto getDefaultMinimumInliningTargetVersion = [&](const llvm::Triple &triple) -> llvm::VersionTuple { -#if SWIFT_DEFAULT_TARGET_MIN_INLINING_VERSION_TO_ABI - // In ABI-stable modules, default to the version where the target's ABI - // was first frozen; older versions will use that one's backwards - // compatibility libraries. +#if SWIFT_DEFAULT_TARGET_MIN_INLINING_VERSION_TO_MIN + // In ABI-stable modules, default to the version when Swift first became + // available. if (FrontendOpts.EnableLibraryEvolution) - if (auto abiStability = minimumABIStableOSVersionForTriple(triple)) - // FIXME: Should we raise it to the minimum supported OS version for - // architectures which were born ABI-stable? - return *abiStability; + if (auto minTriple = minimumAvailableOSVersionForTriple(triple)) + return minTriple; #endif // In ABI-unstable modules, we will never have to interoperate with @@ -834,10 +831,10 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, if (!A) return None; + if (StringRef(A->getValue()) == "min") + return minimumAvailableOSVersionForTriple(Opts.Target); if (StringRef(A->getValue()) == "target") return Opts.getMinPlatformVersion(); - if (StringRef(A->getValue()) == "abi") - return minimumABIStableOSVersionForTriple(Opts.Target); if (auto vers = version::Version::parseVersionString(A->getValue(), SourceLoc(), &Diags)) diff --git a/test/attr/attr_inlinable_available.swift b/test/attr/attr_inlinable_available.swift index acfb6e5f7efd2..74ac804c3ced0 100644 --- a/test/attr/attr_inlinable_available.swift +++ b/test/attr/attr_inlinable_available.swift @@ -11,10 +11,9 @@ // REQUIRES: swift_stable_abi - // Primary execution of this test. Uses the default minimum inlining version, -// which is the version when the ABI became stable. -// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-library-evolution -target %target-next-stable-abi-triple -target-min-inlining-version abi +// which is the version when Swift was introduced. +// RUN: %target-typecheck-verify-swift -swift-version 5 -enable-library-evolution -target %target-next-stable-abi-triple -target-min-inlining-version min // Check that these rules are only applied when requested and that at least some @@ -34,12 +33,12 @@ public struct NoAvailable { @usableFromInline internal init() {} } -@available(macOS 10.14.3, iOS 12.1, tvOS 12.1, watchOS 5.1, *) +@available(macOS 10.9, iOS 7.0, tvOS 8.0, watchOS 1.0, *) public struct BeforeInliningTarget { @usableFromInline internal init() {} } -@available(macOS 10.14.4, iOS 12.2, tvOS 12.2, watchOS 5.2, *) +@available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) public struct AtInliningTarget { @usableFromInline internal init() {} } @@ -90,7 +89,7 @@ public func deployedUseNoAvailable( // expected-note 5 {{add @available attribut } } -@available(macOS 10.14.3, iOS 12.1, tvOS 12.1, watchOS 5.1, *) +@available(macOS 10.9, iOS 7.0, tvOS 8.0, watchOS 1.0, *) public func deployedUseBeforeInliningTarget( _: NoAvailable, _: BeforeInliningTarget, @@ -115,7 +114,7 @@ public func deployedUseBeforeInliningTarget( } } -@available(macOS 10.14.4, iOS 12.2, tvOS 12.2, watchOS 5.2, *) +@available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) public func deployedUseAtInliningTarget( _: NoAvailable, _: BeforeInliningTarget, @@ -248,7 +247,7 @@ public func deployedUseAfterDeploymentTarget( } } -@available(macOS 10.14.3, iOS 12.1, tvOS 12.1, watchOS 5.1, *) +@available(macOS 10.9, iOS 7.0, tvOS 8.0, watchOS 1.0, *) @inlinable public func inlinedUseBeforeInliningTarget( _: NoAvailable, _: BeforeInliningTarget, @@ -264,7 +263,7 @@ public func deployedUseAfterDeploymentTarget( _ = NoAvailable() _ = BeforeInliningTarget() _ = AtInliningTarget() - _ = BetweenTargets() // expected-error {{'BetweenTargets' is only available in}} {{18-25=10.14.5}} || {{31-35=12.3}} || {{42-46=12.3}} || {{56-59=5.3}} + _ = BetweenTargets() // expected-error {{'BetweenTargets' is only available in}} expected-note {{add 'if #available'}} _ = AtDeploymentTarget() // expected-error {{'AtDeploymentTarget' is only available in}} expected-note {{add 'if #available'}} _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in}} expected-note {{add 'if #available'}} @@ -279,7 +278,7 @@ public func deployedUseAfterDeploymentTarget( } } -@available(macOS 10.14.4, iOS 12.2, tvOS 12.2, watchOS 5.2, *) +@available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) @inlinable public func inlinedUseAtInliningTarget( _: NoAvailable, _: BeforeInliningTarget, @@ -295,7 +294,7 @@ public func deployedUseAfterDeploymentTarget( _ = NoAvailable() _ = BeforeInliningTarget() _ = AtInliningTarget() - _ = BetweenTargets() // expected-error {{'BetweenTargets' is only available in}} {{18-25=10.14.5}} || {{31-35=12.3}} || {{42-46=12.3}} || {{56-59=5.3}} + _ = BetweenTargets() // expected-error {{'BetweenTargets' is only available in}} expected-note {{add 'if #available'}} _ = AtDeploymentTarget() // expected-error {{'AtDeploymentTarget' is only available in}} expected-note {{add 'if #available'}} _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in}} expected-note {{add 'if #available'}} @@ -426,6 +425,40 @@ internal func fn() { } } +// @_backDeploy acts like @inlinable. + +@available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) +@_backDeploy(macOS 999.0, iOS 999.0, tvOS 999.0, watchOS 999.0) +public func backDeployedToInliningTarget( + _: NoAvailable, + _: BeforeInliningTarget, + _: AtInliningTarget, + _: BetweenTargets, // expected-error {{'BetweenTargets' is only available in}} + _: AtDeploymentTarget, // expected-error {{'AtDeploymentTarget' is only available in}} + _: AfterDeploymentTarget // expected-error {{'AfterDeploymentTarget' is only available in}} +) { + defer { + _ = AtDeploymentTarget() // expected-error {{'AtDeploymentTarget' is only available in}} expected-note {{add 'if #available'}} + _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in}} expected-note {{add 'if #available'}} + } + _ = NoAvailable() + _ = BeforeInliningTarget() + _ = AtInliningTarget() + _ = BetweenTargets() // expected-error {{'BetweenTargets' is only available in}} expected-note {{add 'if #available'}} + _ = AtDeploymentTarget() // expected-error {{'AtDeploymentTarget' is only available in}} expected-note {{add 'if #available'}} + _ = AfterDeploymentTarget() // expected-error {{'AfterDeploymentTarget' is only available in}} expected-note {{add 'if #available'}} + + if #available(macOS 10.14.5, iOS 12.3, tvOS 12.3, watchOS 5.3, *) { + _ = BetweenTargets() + } + if #available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) { + _ = AtDeploymentTarget() + } + if #available(macOS 11, iOS 14, tvOS 14, watchOS 7, *) { + _ = AfterDeploymentTarget() + } +} + // Default arguments act like @inlinable. public func defaultArgsUseNoAvailable( // expected-note 3 {{add @available attribute}} From 591f7ed9d5c3793d57c646d665ccad00dd740355 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 15 Mar 2022 12:04:36 +0900 Subject: [PATCH 061/242] [Concurrency] Fix typo in internal variable name deadine -> deadline --- stdlib/public/Concurrency/TaskSleep.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/Concurrency/TaskSleep.swift b/stdlib/public/Concurrency/TaskSleep.swift index d9937ae268f3f..665f30135373f 100644 --- a/stdlib/public/Concurrency/TaskSleep.swift +++ b/stdlib/public/Concurrency/TaskSleep.swift @@ -417,10 +417,10 @@ extension Task where Success == Never, Failure == Never { /// @available(SwiftStdlib 5.7, *) public static func sleep( - until deadine: C.Instant, + until deadline: C.Instant, tolerance: C.Instant.Duration? = nil, clock: C ) async throws { - try await clock.sleep(until: deadine, tolerance: tolerance) + try await clock.sleep(until: deadline, tolerance: tolerance) } } From 0a9a5c61c8a1b3bd9629daf465fa6292868e5968 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 14 Mar 2022 20:14:41 -0700 Subject: [PATCH 062/242] Diagnose 'case nil' in Non-Optional Switches This is an anti-pattern since the resulting value will never compare equal to `nil`, and the entire switch-case is dead. This appears to be a misfeature as the subject value is simply type-checked against an optional which produces an injection which matches the global ~= for Optionals. Effectively, `case nil:` becomes `case $match ~= nil`. rdar://89742267 --- lib/Sema/TypeCheckPattern.cpp | 14 +++++++++----- test/stmt/switch_nil.swift | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 test/stmt/switch_nil.swift diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 404e4380fd4c2..8e848941cf60c 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1269,10 +1269,10 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, case PatternKind::Expr: { assert(cast(P)->isResolved() && "coercing unresolved expr pattern!"); + auto *EP = cast(P); if (type->isBool()) { // The type is Bool. // Check if the pattern is a Bool literal - auto EP = cast(P); if (auto *BLE = dyn_cast( EP->getSubExpr()->getSemanticsProvidingExpr())) { P = new (Context) BoolPattern(BLE->getLoc(), BLE->getValue()); @@ -1282,9 +1282,8 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, } // case nil is equivalent to .none when switching on Optionals. - if (type->getOptionalObjectType()) { - auto EP = cast(P); - if (auto *NLE = dyn_cast(EP->getSubExpr())) { + if (auto *NLE = dyn_cast(EP->getSubExpr())) { + if (type->getOptionalObjectType()) { auto *NoneEnumElement = Context.getOptionalNoneDecl(); auto *BaseTE = TypeExpr::createImplicit(type, Context); P = new (Context) EnumElementPattern( @@ -1292,10 +1291,15 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, NoneEnumElement->createNameRef(), NoneEnumElement, nullptr); return TypeChecker::coercePatternToType( pattern.forSubPattern(P, /*retainTopLevel=*/true), type, options); + } else { + // ...but for non-optional types it can never match! Diagnose it. + diags.diagnose(NLE->getLoc(), + diag::value_type_comparison_with_nil_illegal, type); + return nullptr; } } - if (TypeChecker::typeCheckExprPattern(cast(P), dc, type)) + if (TypeChecker::typeCheckExprPattern(EP, dc, type)) return nullptr; return P; diff --git a/test/stmt/switch_nil.swift b/test/stmt/switch_nil.swift new file mode 100644 index 0000000000000..ba642824448c7 --- /dev/null +++ b/test/stmt/switch_nil.swift @@ -0,0 +1,15 @@ +// RUN: %target-typecheck-verify-swift + +enum Hey { + case listen +} + +func test() { + switch Hey.listen { + case nil: // expected-error{{type 'Hey' is not optional, value can never be nil}} + break + default: + break + } +} + From 3a41bcdeaa75b0d88577a13dd7635efc34a6275c Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Mon, 14 Mar 2022 21:01:17 -0700 Subject: [PATCH 063/242] [cxx-interop][docs] add a C++ interoperability status document --- .../CppInteroperabilityStatus.md | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 docs/CppInteroperability/CppInteroperabilityStatus.md diff --git a/docs/CppInteroperability/CppInteroperabilityStatus.md b/docs/CppInteroperability/CppInteroperabilityStatus.md new file mode 100644 index 0000000000000..bbd82dad64cef --- /dev/null +++ b/docs/CppInteroperability/CppInteroperabilityStatus.md @@ -0,0 +1,102 @@ +# C++ Interoperability Status + +Swift has some experimental ability to interoperate with C++. +This document provides an overview of the status of the Swift and C++ interoperability support. + +## C++ to Swift Interoperability Status + +Swift has the experimental ability to import a large subset of C++. +This section of the document describes which C++ language and standard library features can be imported and used from Swift in an experimental manner. + +### Importing C++ + +There are currently two experimental ways to import C++ into Swift: +- **Clang modules**: can be imported into Swift. This requires a module map. +- **Bridging header**: can be imported into Swift. Headers included in the bridging header will be imported. +Please note that support for importing C++ 20 modules isn’t implemented. + +Both CMake and the Swift package manager can be configured to invoke Swift with the correct arguments to import C++ headers. + +**Note**: C++ code is imported using the Objective-C++ language mode on Apple platforms. + +### Experimental C++ Language Support + +This status table describes which of the following C++ language features can be used in Swift: + +| **C++ Language Feature** | **Implemented Experimental Support For Using It In Swift** | +|---------------------------------------------|---------------------------------------------------------------| +| Top-level functions | Yes | +| Enumerations | Yes. That includes `enum class` | +| Struct / Class types | Yes - as value types, except for types without a copy constructor. Partial experimental support for importing a C++ struct/class as a reference type | +| Typedefs / Type aliases | Yes | +| Global Variables | Yes | +| Namespaces | Yes | +| Inline Namespaces | Yes, with some known issues | +| Exceptions | No | +| Fields | Yes | +| Member functions | Yes. Some value category overloads aren't imported | +| Virtual Member Functions | No | +| Operators | Yes, with some known issues | +| Subscript Operators | Yes | +| Constructors | Yes. That includes implicit constructors | +| Destructor | Yes. C++ destructors are invoked automatically when the value is no longer used in Swift | +| Copy constructor / copy assignment operator | Yes. Swift invokes the underlying copy constructor when copying a C++ value | +| Move constructor / move assignment operator | No | +| Base class member functions / operators | Yes, with some known issues | +| Function templates | Yes | +| Class templates | Yes | +| Dependent types | Partially: imported as Any | +| Availability Attributes | Yes | + + +The following C++ code patterns or language features have specific mappings to Swift language features when imported in Swift: + + +| **C++ Language Feature** | **Imported Into Swift** | +|------------------------------------------------------|------------------------------------------------------------------------------------------| +| `get`/`set` member functions | Imported as computed property (since Swift-5.7) | +| `const`/non-`const` member function overload set | Both overloads are imported as a method, with non-`const` method being renamed to `mutating…`. (since Swift-5.7). This will change in a future version of Swift | + + +Unless stated otherwise (i.e., imported reference types) all Swift features work with imported types. For example: use in generic contexts, protocol conformance, extensions, etc. + + +### C++ Standard Library Support + +Libc++ can be imported and used from Swift. + +This status table describes which of the following C++ standard library features have some experimental support for using them in Swift: + +| **C++ Standard Library Feature** | **Can Be Used From Swift** | +|------------------------------------|----------------------------------------------| +| `std::string` | Yes | +| `std::vector` | Yes | + + +## Known Issues + +### Inline Namespaces +- https://bugs.swift.org/browse/SR-15956: Swift's typechecker currently doesn't allow calling a function from an inline namespace when it's referenced through the parent namespace. Example of a test that fails: https://github.com/apple/swift/blob/main/test/Interop/Cxx/namespace/inline-namespace-function-call-broken.swift + + +## Swift to C++ Interoperability Status + +This section of the document describes which Swift language and standard library features can be imported and used from C++. + +### Importing Swift + +Swift has some experimental support for generating a header that can be imported by C++. + +### Swift Language Support + +This status table describes which of the following Swift language features have some experimental support for using them in C++. + +**Functions** + +| **Swift Language Feature** | **Implemented Experimental Support For Using It In C++** | +|--------------------------------|----------------------------------------------------------| +| Top-level `@_cdecl` functions | Yes | +| Top-level Swift functions | Partially, only with C compatible types | +| `inout` parameters | No | +| Variadic parameters | No | +| Multiple return values | No | From 14bbd27e83fabd69bfc7c0faa687f2f9ee057878 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 14 Mar 2022 09:46:51 +0900 Subject: [PATCH 064/242] [Distributed] remove a few warnings --- lib/SILGen/SILGenDistributed.cpp | 2 -- lib/Sema/CodeSynthesisDistributedActor.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/lib/SILGen/SILGenDistributed.cpp b/lib/SILGen/SILGenDistributed.cpp index e687b0eb3e9ca..0acf739f552ee 100644 --- a/lib/SILGen/SILGenDistributed.cpp +++ b/lib/SILGen/SILGenDistributed.cpp @@ -43,8 +43,6 @@ using namespace Lowering; /// or the subsequent cast to VarDecl failed. static VarDecl* lookupProperty(NominalTypeDecl *decl, DeclName name) { assert(decl && "decl was null"); - auto &C = decl->getASTContext(); - if (auto clazz = dyn_cast(decl)) { auto refs = decl->lookupDirect(name); if (refs.size() != 1) diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 96eed482a31c6..a9737f8d566b4 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -564,7 +564,6 @@ addDistributedActorCodableConformance( assert(proto->isSpecificProtocol(swift::KnownProtocolKind::Decodable) || proto->isSpecificProtocol(swift::KnownProtocolKind::Encodable)); auto &C = actor->getASTContext(); - auto DC = actor->getDeclContext(); auto module = actor->getParentModule(); // === Only Distributed actors can gain this implicit conformance From 8925f9d8fb2cf6946b434e51057fdb0f2f153bc9 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 14 Mar 2022 13:32:57 +0900 Subject: [PATCH 065/242] [Distributed] recordErrorType is not ad-hoc requirement anymore --- lib/Sema/TypeCheckDistributed.cpp | 17 +---------------- .../Distributed/DistributedActorSystem.swift | 8 +++----- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp index 8bd39063ebde1..80aa6d47c29ab 100644 --- a/lib/Sema/TypeCheckDistributed.cpp +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -310,22 +310,7 @@ bool swift::checkDistributedActorSystemAdHocProtocolRequirements( } if (checkAdHocRequirementAccessControl(decl, Proto, recordArgumentDecl)) anyMissingAdHocRequirements = true; - - // - recordErrorType - auto recordErrorTypeDecl = C.getRecordErrorTypeOnDistributedInvocationEncoder(decl); - if (!recordErrorTypeDecl) { - auto identifier = C.Id_recordErrorType; - decl->diagnose( - diag::distributed_actor_system_conformance_missing_adhoc_requirement, - decl->getDescriptiveKind(), decl->getName(), identifier); - decl->diagnose(diag::note_distributed_actor_system_conformance_missing_adhoc_requirement, - decl->getName(), identifier, - "mutating func recordErrorType(_ errorType: Err.Type) throws\n"); - anyMissingAdHocRequirements = true; - } - if (checkAdHocRequirementAccessControl(decl, Proto, recordErrorTypeDecl)) - anyMissingAdHocRequirements = true; - + // - recordReturnType auto recordReturnTypeDecl = C.getRecordReturnTypeOnDistributedInvocationEncoder(decl); if (!recordReturnTypeDecl) { diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index 474fe7086ba36..0cfe4cc918890 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -407,11 +407,9 @@ public protocol DistributedTargetInvocationEncoder { // mutating func recordArgument(_ argument: Argument) throws // TODO(distributed): offer recordArgument(label:type:) -// /// Ad-hoc requirement -// /// -// /// Record the error type of the distributed method. -// /// This method will not be invoked if the target is not throwing. -// mutating func recordErrorType(_ type: E.Type) throws // TODO: make not adhoc + /// Record the error type of the distributed method. + /// This method will not be invoked if the target is not throwing. + mutating func recordErrorType(_ type: E.Type) throws // /// Ad-hoc requirement // /// From 4fa0855907c59d845bfb124f0258adec506b891a Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 14 Mar 2022 21:01:43 +0900 Subject: [PATCH 066/242] [Distributed] RemoteCallTarget now pretty prints and hides mangled names --- include/swift/Runtime/HeapObject.h | 9 ++ lib/Sema/TypeCheckDistributed.cpp | 2 +- .../Distributed/DistributedActorSystem.swift | 98 +++++++----- stdlib/public/core/Misc.swift | 29 ++++ stdlib/public/runtime/Casting.cpp | 141 ++++++++++++++++++ .../Inputs/BadDistributedActorSystems.swift | 8 +- .../Inputs/FakeDistributedActorSystems.swift | 8 +- ...ted_actor_func_calls_remoteCall_echo.swift | 2 +- ...ed_actor_func_calls_remoteCall_empty.swift | 2 +- ...or_func_calls_remoteCall_genericFunc.swift | 4 +- ...ed_actor_func_calls_remoteCall_hello.swift | 2 +- ...ted_actor_func_calls_remoteCall_take.swift | 2 +- ...unc_calls_remoteCall_takeThrowReturn.swift | 2 +- ...actor_func_calls_remoteCall_take_two.swift | 2 +- ...ed_actor_func_calls_remoteCall_throw.swift | 2 +- .../distributed_actor_remoteCall.swift | 28 ++-- ...moteCallTarget_demanglingTargetNames.swift | 80 ++++++++++ .../distributed_actor_remote_functions.swift | 22 +-- ...stem_missing_adhoc_requirement_impls.swift | 37 +++-- 19 files changed, 376 insertions(+), 104 deletions(-) create mode 100644 test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift diff --git a/include/swift/Runtime/HeapObject.h b/include/swift/Runtime/HeapObject.h index e3487eaef3ced..efb50b3f93c57 100644 --- a/include/swift/Runtime/HeapObject.h +++ b/include/swift/Runtime/HeapObject.h @@ -1101,6 +1101,15 @@ swift_getTypeName(const Metadata *type, bool qualified); /// -> (UnsafePointer, Int) SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API TypeNamePair +swift_getFunctionFullNameFromMangledName( + const char *mangledNameStart, uintptr_t mangledNameLength); + +/// Return the human-readable full name of the mangled function name passed in. +/// func _getMangledTypeName(_ mangledName: UnsafePointer, +/// mangledNameLength: UInt) +/// -> (UnsafePointer, Int) +SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_API +TypeNamePair swift_getMangledTypeName(const Metadata *type); } // end namespace swift diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp index 80aa6d47c29ab..f8bec3cdf3df0 100644 --- a/lib/Sema/TypeCheckDistributed.cpp +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -310,7 +310,7 @@ bool swift::checkDistributedActorSystemAdHocProtocolRequirements( } if (checkAdHocRequirementAccessControl(decl, Proto, recordArgumentDecl)) anyMissingAdHocRequirements = true; - + // - recordReturnType auto recordReturnTypeDecl = C.getRecordReturnTypeOnDistributedInvocationEncoder(decl); if (!recordReturnTypeDecl) { diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index 0cfe4cc918890..d57f6bead91ef 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -172,23 +172,18 @@ extension DistributedActorSystem { /// latency sensitive-use-cases. public func executeDistributedTarget( on actor: Act, - mangledTargetName: String, + target: RemoteCallTarget, invocationDecoder: inout InvocationDecoder, handler: ResultHandler ) async throws where Act: DistributedActor, // Act.ID == ActorID, // FIXME(distributed): can we bring this back? ResultHandler: DistributedTargetInvocationResultHandler { - // NOTE: this implementation is not the most efficient, nor final, version of this func - // we end up demangling the name multiple times, perform more heap allocations than - // we truly need to etc. We'll eventually move this implementation to a specialized one - // avoiding these issues. - guard mangledTargetName.count > 0 && mangledTargetName.first == "$" else { + // Get the expected parameter count of the func + guard let targetName = target.identifier else { throw ExecuteDistributedTargetError( - message: "Illegal mangledTargetName detected, must start with '$'") + message: "Unable to extract target identifier from remote call target: \(target)") } - - // Get the expected parameter count of the func - let nameUTF8 = Array(mangledTargetName.utf8) + let nameUTF8 = Array(targetName.utf8) // Gen the generic environment (if any) associated with the target. let genericEnv = nameUTF8.withUnsafeBufferPointer { nameUTF8 in @@ -207,7 +202,9 @@ extension DistributedActorSystem { if let genericEnv = genericEnv { let subs = try invocationDecoder.decodeGenericSubstitutions() - + for item in subs { + print("SUB: \(item)") + } if subs.isEmpty { throw ExecuteDistributedTargetError( message: "Cannot call generic method without generic argument substitutions") @@ -224,7 +221,7 @@ extension DistributedActorSystem { genericArguments: substitutionsBuffer!) if numWitnessTables < 0 { throw ExecuteDistributedTargetError( - message: "Generic substitutions \(subs) do not satisfy generic requirements of \(mangledTargetName)") + message: "Generic substitutions \(subs) do not satisfy generic requirements of \(target) (\(targetName))") } } @@ -237,7 +234,7 @@ extension DistributedActorSystem { message: """ Failed to decode distributed invocation target expected parameter count, error code: \(paramCount) - mangled name: \(mangledTargetName) + mangled name: \(targetName) """) } @@ -262,7 +259,7 @@ extension DistributedActorSystem { message: """ Failed to decode the expected number of params of distributed invocation target, error code: \(decodedNum) (decoded: \(decodedNum), expected params: \(paramCount) - mangled name: \(mangledTargetName) + mangled name: \(targetName) """) } @@ -280,7 +277,7 @@ extension DistributedActorSystem { return UnsafeRawPointer(UnsafeMutablePointer.allocate(capacity: 1)) } - guard let returnTypeFromTypeInfo: Any.Type = _getReturnTypeInfo(mangledMethodName: mangledTargetName, + guard let returnTypeFromTypeInfo: Any.Type = _getReturnTypeInfo(mangledMethodName: targetName, genericEnv: genericEnv, genericArguments: substitutionsBuffer) else { throw ExecuteDistributedTargetError( @@ -307,7 +304,7 @@ extension DistributedActorSystem { // Execute the target! try await _executeDistributedTarget( on: actor, - mangledTargetName, UInt(mangledTargetName.count), + targetName, UInt(targetName.count), argumentDecoder: &invocationDecoder, argumentTypes: argumentTypesBuffer.baseAddress!._rawValue, resultBuffer: resultBuffer._rawValue, @@ -326,6 +323,52 @@ extension DistributedActorSystem { } } +/// Represents a 'target' of a distributed call, such as a `distributed func` or +/// `distributed` computed property. Identification schemes may vary between +/// systems, and are subject to evolution. +/// +/// Actor systems generally should treat the `identifier` as an opaque string, +/// and pass it along to the remote system for in their `remoteCall` +/// implementation. Alternative approaches are possible, where the identifiers +/// are either compressed, cached, or represented in other ways, as long as the +/// recipient system is able to determine which target was intended to be +/// invoked. +/// +/// The string representation will attempt to pretty print the target identifier, +/// however its exact format is not specified and may change in future versions. +@available(SwiftStdlib 5.7, *) +public struct RemoteCallTarget: CustomStringConvertible { + private let _storage: _Storage + private enum _Storage { + case mangledName(String) + } + + // Only intended to be created by the _Distributed library. + // TODO(distributed): make this internal and only allow calling by the synthesized code? + public init(_mangledName: String) { + self._storage = .mangledName(_mangledName) + } + + /// The underlying identifier of the target, returned as-is. + public var identifier: String? { + switch self._storage { + case .mangledName(let name): + return name + } + } + + public var description: String { + switch self._storage { + case .mangledName(let mangledName): + if let name = _getFunctionFullNameFromMangledName(mangledName: mangledName) { + return name + } else { + return "\(mangledName)" + } + } + } +} + @available(SwiftStdlib 5.7, *) @_silgen_name("swift_distributed_execute_target") func _executeDistributedTarget( @@ -339,29 +382,6 @@ func _executeDistributedTarget( numWitnessTables: UInt ) async throws -// ==== ---------------------------------------------------------------------------------------------------------------- -// MARK: Support types -/// A distributed 'target' can be a `distributed func` or `distributed` computed property. -@available(SwiftStdlib 5.7, *) -public struct RemoteCallTarget { // TODO: ship this around always; make printing nice - let _mangledName: String // TODO: StaticString would be better here; no arc, codesize of cleanups - - // Only intended to be created by the _Distributed library. - // TODO(distributed): make this internal and only allow calling by the synthesized code? - public init(_mangledName: String) { - self._mangledName = _mangledName - } - - public var mangledName: String { - _mangledName - } - - // .Base.hello(hi:) - public var fullName: String { // TODO: make description - fatalError("NOT IMPLEMENTED YET: \(#function)") - } -} - /// Used to encode an invocation of a distributed target (method or computed property). /// /// ## Forming an invocation diff --git a/stdlib/public/core/Misc.swift b/stdlib/public/core/Misc.swift index 572443f90cfc3..d1d40ba895bdc 100644 --- a/stdlib/public/core/Misc.swift +++ b/stdlib/public/core/Misc.swift @@ -46,6 +46,35 @@ public func _autorelease(_ x: AnyObject) { } #endif + +@available(SwiftStdlib 5.7, *) +@_silgen_name("swift_getFunctionFullNameFromMangledName") +public // SPI (Distributed) +func _getFunctionFullNameFromMangledNameImpl( + _ mangledName: UnsafePointer, _ mangledNameLength: UInt +) -> (UnsafePointer, UInt) + +/// Given a function's mangled name, return a human readable name. +/// Used e.g. by Distributed.RemoteCallTarget to hide mangled names. +@available(SwiftStdlib 5.7, *) +public // SPI (Distributed) +func _getFunctionFullNameFromMangledName(mangledName: String) -> String? { + let mangledNameUTF8 = Array(mangledName.utf8) + let (stringPtr, count) = + mangledNameUTF8.withUnsafeBufferPointer { (mangledNameUTF8) in + return _getFunctionFullNameFromMangledNameImpl( + mangledNameUTF8.baseAddress!, + UInt(mangledNameUTF8.endIndex)) + } + + guard count > 0 else { + return nil + } + + return String._fromUTF8Repairing( + UnsafeBufferPointer(start: stringPtr, count: Int(count))).0 +} + // FIXME(ABI)#51 : this API should allow controlling different kinds of // qualification separately: qualification with module names and qualification // with type names that we are nested in. diff --git a/stdlib/public/runtime/Casting.cpp b/stdlib/public/runtime/Casting.cpp index 566a1802cbe92..34880edce5f13 100644 --- a/stdlib/public/runtime/Casting.cpp +++ b/stdlib/public/runtime/Casting.cpp @@ -146,6 +146,7 @@ using TypeNameCacheKey = llvm::PointerIntPair #if SWIFT_CASTING_SUPPORTS_MUTEX static StaticReadWriteLock TypeNameCacheLock; +static StaticReadWriteLock MangledToPrettyFunctionNameCacheLock; #endif /// Cache containing rendered names for Metadata. @@ -153,6 +154,11 @@ static StaticReadWriteLock TypeNameCacheLock; static Lazy>> TypeNameCache; +/// Cache containing rendered human-readable names for incoming mangled names. +static Lazy>> +/// Access MUST be protected using `MangledToPrettyFunctionNameCache`. + MangledToPrettyFunctionNameCache; + TypeNamePair swift::swift_getTypeName(const Metadata *type, bool qualified) { TypeNameCacheKey key = TypeNameCacheKey(type, qualified ? TypeNameKind::Qualified: TypeNameKind::NotQualified); @@ -255,6 +261,141 @@ swift::swift_getMangledTypeName(const Metadata *type) { } } + +TypeNamePair +swift::swift_getFunctionFullNameFromMangledName( + const char *mangledNameStart, uintptr_t mangledNameLength) { + llvm::StringRef mangledName(mangledNameStart, mangledNameLength); + + auto &cache = MangledToPrettyFunctionNameCache.get(); + // Attempt read-only lookup of cache entry. + { + #if SWIFT_CASTING_SUPPORTS_MUTEX + StaticScopedReadLock guard(MangledToPrettyFunctionNameCacheLock); + #endif + + auto found = cache.find(mangledName); + if (found != cache.end()) { + auto result = found->second; + return TypeNamePair{result.first, result.second}; + } + } + + for (char c : mangledName) { + if (c >= '\x01' && c <= '\x1F') + return TypeNamePair{nullptr, 0}; + } + + // Read-only lookup failed, we may need to demangle and cache the entry. + // We have to copy the string to be able to refer to it "forever": + auto copy = (char *)malloc(mangledNameLength); + memcpy(copy, mangledNameStart, mangledNameLength); + mangledName = StringRef(copy, mangledNameLength); + + std::string demangled; + StackAllocatedDemangler<1024> Dem; + NodePointer node = Dem.demangleSymbol(mangledName); + if (!node) { + return TypeNamePair{nullptr, 0}; + } + + // Form the demangled string from the node tree. + node = node->findByKind(Demangle::Node::Kind::Function, /*maxDepth=*/3); + if (!node || node->getNumChildren() < 3) { + // we normally expect Class/Identifier/Type, but don't need `Type` + return TypeNamePair{nullptr, 0}; + } + + // Class identifier: + auto clazz = node->findByKind(Demangle::Node::Kind::Class, 1); + if (clazz) { + if (auto module = clazz->findByKind(Demangle::Node::Kind::Module, 1)) { + demangled += module->getText(); + demangled += "."; + } + if (auto clazzIdent = clazz->findByKind(Demangle::Node::Kind::Identifier, 1)) { + demangled += clazzIdent->getText(); + demangled += "."; + } + } + + // Function identifier: + NodePointer funcIdent = nullptr; // node == Function + for (size_t i = 0; i < node->getNumChildren(); ++i) { + if (node->getChild(i)->getKind() == Demangle::Node::Kind::Identifier) { + funcIdent = node->getChild(i); + } + } + + // We always expect to work with functions here and they must have idents + if (!funcIdent) { + return TypeNamePair{nullptr, 0}; + } + assert(funcIdent->getKind() == Demangle::Node::Kind::Identifier); + demangled += funcIdent->getText(); + demangled += "("; + + if (auto labelList = node->findByKind(Demangle::Node::Kind::LabelList, /*maxDepth=*/1)) { + if (labelList->getNumChildren()) { + size_t paramIdx = 0; + while (paramIdx < labelList->getNumChildren()) { + auto labelIdentifier = labelList->getChild(paramIdx++); + if (labelIdentifier) { + if (labelIdentifier->getKind() == Demangle::Node::Kind::Identifier) { + demangled += labelIdentifier->getText(); + demangled += ":"; + } else if (labelIdentifier->getKind() == + Demangle::Node::Kind::FirstElementMarker) { + demangled += "_:"; + } + } + } + } else if (auto argumentTuple = node->findByKind( + Demangle::Node::Kind::ArgumentTuple, /*maxDepth=*/5)) { + // LabelList was empty. + // + // The function has no labels at all, but could have some parameters... + // we need to check for their count, and render it as e.g. (::) for two + // anonymous parameters. + auto params = argumentTuple->getFirstChild(); + if (auto paramsType = params->getFirstChild()) { + if (paramsType->getKind() != Demangle::Node::Kind::Tuple) { + // was a single, unnamed, parameter + demangled += "_:"; + } else { + // there are a few parameters; find out how many + while (params && params->getFirstChild() && + params->getFirstChild()->getKind() != + Demangle::Node::Kind::TupleElement) { + params = params->getFirstChild(); + } + if (params) { + for (size_t i = 0; i < params->getNumChildren(); ++i) { + demangled += "_:"; + } + } + } + } + } + } + demangled += ")"; + + // We have to copy the string to be able to refer to it; + auto size = demangled.size(); + auto result = (char *)malloc(size + 1); + memcpy(result, demangled.data(), size); + result[size] = 0; // 0-terminated string + + { + #if SWIFT_CASTING_SUPPORTS_MUTEX + StaticScopedWriteLock guard(MangledToPrettyFunctionNameCacheLock); + #endif + + cache.insert({mangledName, {result, size}}); + return TypeNamePair{result, size}; + } +} + /// Report a dynamic cast failure. // This is noinline to preserve this frame in stack traces. // We want "dynamicCastFailure" to appear in crash logs even we crash diff --git a/test/Distributed/Inputs/BadDistributedActorSystems.swift b/test/Distributed/Inputs/BadDistributedActorSystems.swift index d40693b3838be..26ba103035ea7 100644 --- a/test/Distributed/Inputs/BadDistributedActorSystems.swift +++ b/test/Distributed/Inputs/BadDistributedActorSystems.swift @@ -87,7 +87,7 @@ public struct MissingRemoteCallVoidActorSystem: DistributedActorSystem { Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } // func remoteCallVoid( @@ -99,7 +99,7 @@ public struct MissingRemoteCallVoidActorSystem: DistributedActorSystem { // where Act: DistributedActor, // Act.ID == ActorID, // Err: Error { -// throw ExecuteDistributedTargetError(message: "Not implemented.") +// throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") // } } @@ -177,7 +177,7 @@ public final class FakeRoundtripActorSystem: DistributedActorSystem, @unchecked } try await executeDistributedTarget( on: active, - mangledTargetName: target.mangledName, + target: target, invocationDecoder: invocation.makeDecoder(), handler: resultHandler ) @@ -224,7 +224,7 @@ public final class FakeRoundtripActorSystem: DistributedActorSystem, @unchecked } try await executeDistributedTarget( on: active, - mangledTargetName: target.mangledName, + target: target, invocationDecoder: invocation.makeDecoder(), handler: resultHandler ) diff --git a/test/Distributed/Inputs/FakeDistributedActorSystems.swift b/test/Distributed/Inputs/FakeDistributedActorSystems.swift index 385477d54cca6..b7f276b903cef 100644 --- a/test/Distributed/Inputs/FakeDistributedActorSystems.swift +++ b/test/Distributed/Inputs/FakeDistributedActorSystems.swift @@ -87,7 +87,7 @@ public struct FakeActorSystem: DistributedActorSystem, CustomStringConvertible { Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } public func remoteCallVoid( @@ -99,7 +99,7 @@ public struct FakeActorSystem: DistributedActorSystem, CustomStringConvertible { where Act: DistributedActor, Act.ID == ActorID, Err: Error { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } public nonisolated var description: Swift.String { @@ -184,7 +184,7 @@ public final class FakeRoundtripActorSystem: DistributedActorSystem, @unchecked try await executeDistributedTarget( on: active, - mangledTargetName: target.mangledName, + target: target, invocationDecoder: &decoder, handler: resultHandler ) @@ -234,7 +234,7 @@ public final class FakeRoundtripActorSystem: DistributedActorSystem, @unchecked try await executeDistributedTarget( on: active, - mangledTargetName: target.mangledName, + target: target, invocationDecoder: &decoder, handler: resultHandler ) diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_echo.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_echo.swift index 425b83e6589f9..6583b545faadd 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_echo.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_echo.swift @@ -32,7 +32,7 @@ func test() async throws { let ref = try Greeter.resolve(id: local.id, using: system) let reply = try await ref.echo(name: "Caplin") - // CHECK: >> remoteCall: on:main.Greeter, target:RemoteCallTarget(_mangledName: "$s4main7GreeterC4echo4nameS2S_tYaKFTE"), invocation:FakeInvocationEncoder(genericSubs: [], arguments: ["Caplin"], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String + // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.echo(name:), invocation:FakeInvocationEncoder(genericSubs: [], arguments: ["Caplin"], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String // CHECK: << remoteCall return: Echo: Caplin print("reply: \(reply)") diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_empty.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_empty.swift index e5505a1401b5f..9d37fdab58e38 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_empty.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_empty.swift @@ -31,7 +31,7 @@ func test() async throws { let ref = try Greeter.resolve(id: local.id, using: system) try await ref.empty() - // CHECK: >> remoteCallVoid: on:main.Greeter, target:RemoteCallTarget(_mangledName: "$s4main7GreeterC5emptyyyYaKFTE"), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: nil, errorType: nil), throwing:Swift.Never + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.empty(), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: nil, errorType: nil), throwing:Swift.Never // CHECK: << onReturn: () } diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift index a7635d6af064c..db62c1a0c35b3 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift @@ -43,7 +43,7 @@ func test() async throws { // CHECK: > encode argument: Caplin // CHECK: > encode return type: Swift.String // CHECK: > done recording - // CHECK: >> remoteCall: on:main.Greeter, target:RemoteCallTarget(_mangledName: "$s4main7GreeterC7genericySSxYaKSeRzSERzlFTE"), invocation:FakeInvocationEncoder(genericSubs: [Swift.String], arguments: ["Caplin"], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String + // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.generic(_:), invocation:FakeInvocationEncoder(genericSubs: [Swift.String], arguments: ["Caplin"], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String print("reply: \(r1)") // CHECK: reply: Caplin @@ -59,7 +59,7 @@ func test() async throws { // CHECK: > encode argument: [1, 2, 3] // CHECK: > encode return type: Swift.String // CHECK: > done recording - // CHECK: >> remoteCall: on:main.Greeter, target:RemoteCallTarget(_mangledName: "$s4main7GreeterC8generic26strict__SSSd_xSayq_GtYaKSeRzSERzSeR_SER_r0_lFTE"), invocation:FakeInvocationEncoder(genericSubs: [Swift.String, Swift.Int], arguments: [2.0, "Caplin", [1, 2, 3]], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String + // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.generic2(strict:_:_:), invocation:FakeInvocationEncoder(genericSubs: [Swift.String, Swift.Int], arguments: [2.0, "Caplin", [1, 2, 3]], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String print("reply: \(r2)") // CHECK: reply: Caplin } diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_hello.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_hello.swift index 12995b7a52f9c..623161522b23a 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_hello.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_hello.swift @@ -32,7 +32,7 @@ func test() async throws { let ref = try Greeter.resolve(id: local.id, using: system) let response = try await ref.hello() - // CHECK: >> remoteCall: on:main.Greeter, target:RemoteCallTarget(_mangledName: "$s4main7GreeterC5helloSSyYaKFTE"), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String + // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.hello(), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String print("response: \(response)") // CHECK: response: Hello, World! diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take.swift index 2716946afec5a..092d207a74b22 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take.swift @@ -32,7 +32,7 @@ func test() async throws { let ref = try Greeter.resolve(id: local.id, using: system) try await ref.take(name: "Caplin") - // CHECK: >> remoteCallVoid: on:main.Greeter, target:RemoteCallTarget(_mangledName: "$s4main7GreeterC4take4nameySS_tYaKFTE"), invocation:FakeInvocationEncoder(genericSubs: [], arguments: ["Caplin"], returnType: nil, errorType: nil), throwing:Swift.Never + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.take(name:), invocation:FakeInvocationEncoder(genericSubs: [], arguments: ["Caplin"], returnType: nil, errorType: nil), throwing:Swift.Never // CHECK: take: Caplin } diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_takeThrowReturn.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_takeThrowReturn.swift index 79d90abb81f4b..222f6d40d378a 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_takeThrowReturn.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_takeThrowReturn.swift @@ -35,7 +35,7 @@ func test() async throws { do { let value = try await ref.takeThrowReturn(name: "Example") - // CHECK: >> remoteCall: on:main.Greeter, target:RemoteCallTarget(_mangledName: "$s4main7GreeterC15takeThrowReturn4nameS2S_tYaKFTE"), invocation:FakeInvocationEncoder(genericSubs: [], arguments: ["Example"], returnType: Optional(Swift.String), errorType: Optional(Swift.Error)), throwing:Swift.Error, returning:Swift.String + // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.takeThrowReturn(name:), invocation:FakeInvocationEncoder(genericSubs: [], arguments: ["Example"], returnType: Optional(Swift.String), errorType: Optional(Swift.Error)), throwing:Swift.Error, returning:Swift.String print("did not throw") // CHECK-NOT: did not throw diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift index 19de7df32e262..97179d10be875 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift @@ -38,7 +38,7 @@ func test() async throws { let ref = try Greeter.resolve(id: local.id, using: system) try await ref.take(name: "Caplin", int: 1337) - // CHECK: >> remoteCallVoid: on:main.Greeter, target:RemoteCallTarget(_mangledName: "$s4main7GreeterC4take4name3intySS_SitYaKFTE"), invocation:FakeInvocationEncoder(genericSubs: [], arguments: ["Caplin", 1337], returnType: nil, errorType: nil), throwing:Swift.Never + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.take(name:int:), invocation:FakeInvocationEncoder(genericSubs: [], arguments: ["Caplin", 1337], returnType: nil, errorType: nil), throwing:Swift.Never // try await ref.take(name: "Caplin", int: 1337, clazz: .init()) // FIXME(distributed): crashes diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_throw.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_throw.swift index ad81ce107e07d..6f110d876cdba 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_throw.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_throw.swift @@ -35,7 +35,7 @@ func test() async throws { do { try await ref.maybeThrows() - // CHECK: >> remoteCallVoid: on:main.Greeter, target:RemoteCallTarget(_mangledName: "$s4main7GreeterC11maybeThrowsyyYaKFTE"), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: nil, errorType: Optional(Swift.Error)), throwing:Swift.Error + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.maybeThrows(), invocation:FakeInvocationEncoder(genericSubs: [], arguments: [], returnType: nil, errorType: Optional(Swift.Error)), throwing:Swift.Error print("did not throw") // CHECK-NOT: did not throw diff --git a/test/Distributed/Runtime/distributed_actor_remoteCall.swift b/test/Distributed/Runtime/distributed_actor_remoteCall.swift index 887deb1149c5e..e156d9463f07c 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCall.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCall.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeCodableForDistributedTests.swiftmodule -module-name FakeCodableForDistributedTests -disable-availability-checking %S/../Inputs/FakeCodableForDistributedTests.swift -// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeCodableForDistributedTests.swift -o %t/a.out +// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeCodableForDistributedTests.swift -o %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s --color // REQUIRES: executable_test @@ -304,7 +304,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - mangledTargetName: emptyName, + target: RemoteCallTarget(_mangledName: emptyName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -312,7 +312,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - mangledTargetName: helloName, + target: RemoteCallTarget(_mangledName: helloName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -320,7 +320,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - mangledTargetName: answerName, + target: RemoteCallTarget(_mangledName: answerName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -328,7 +328,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - mangledTargetName: largeResultName, + target: RemoteCallTarget(_mangledName: largeResultName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -336,7 +336,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - mangledTargetName: enumResultName, + target: RemoteCallTarget(_mangledName: enumResultName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -350,7 +350,7 @@ func test() async throws { var echoDecoder = echoInvocation.makeDecoder() try await system.executeDistributedTarget( on: local, - mangledTargetName: echoName, + target: RemoteCallTarget(_mangledName: echoName), invocationDecoder: &echoDecoder, handler: FakeResultHandler() ) @@ -365,7 +365,7 @@ func test() async throws { var generic1Decoder = generic1Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - mangledTargetName: generic1Name, + target: RemoteCallTarget(_mangledName: generic1Name), invocationDecoder: &generic1Decoder, handler: FakeResultHandler() ) @@ -383,7 +383,7 @@ func test() async throws { var generic2Decoder = generic2Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - mangledTargetName: generic2Name, + target: RemoteCallTarget(_mangledName: generic2Name), invocationDecoder: &generic2Decoder, handler: FakeResultHandler() ) @@ -404,7 +404,7 @@ func test() async throws { var generic3Decoder = generic3Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - mangledTargetName: generic3Name, + target: RemoteCallTarget(_mangledName: generic3Name), invocationDecoder: &generic3Decoder, handler: FakeResultHandler() ) @@ -426,7 +426,7 @@ func test() async throws { var generic4Decoder = generic4Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - mangledTargetName: generic4Name, + target: RemoteCallTarget(_mangledName: generic4Name), invocationDecoder: &generic4Decoder, handler: FakeResultHandler() ) @@ -450,7 +450,7 @@ func test() async throws { var generic5Decoder = generic5Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - mangledTargetName: generic5Name, + target: RemoteCallTarget(_mangledName: generic5Name), invocationDecoder: &generic5Decoder, handler: FakeResultHandler() ) @@ -473,7 +473,7 @@ func test() async throws { var genericOptDecoder = genericOptInvocation.makeDecoder() try await system.executeDistributedTarget( on: local, - mangledTargetName: genericOptionalName, + target: RemoteCallTarget(_mangledName: genericOptionalName), invocationDecoder: &genericOptDecoder, handler: FakeResultHandler() ) @@ -488,7 +488,7 @@ func test() async throws { var decodeErrDecoder = decodeErrInvocation.makeDecoder() try await system.executeDistributedTarget( on: local, - mangledTargetName: expectsDecodeErrorName, + target: RemoteCallTarget(_mangledName: expectsDecodeErrorName), invocationDecoder: &decodeErrDecoder, handler: FakeResultHandler() ) diff --git a/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift b/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift new file mode 100644 index 0000000000000..7c0a73d45d873 --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift @@ -0,0 +1,80 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift +// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s --color + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +// FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 +// UNSUPPORTED: windows + +import _Distributed +import FakeDistributedActorSystems + +typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem + +distributed actor Greeter { + distributed func noParams() {} + distributed func noParamsThrows() throws {} + distributed func noLabel(_ value: String) {} + distributed func noLabels2(_ value: String, _ value2: String) {} + distributed func noLabels3(_ value: String, _ value2: String, _ value3: String) {} + distributed func oneLabel(value: String, _ value2: String, _ value3: String) {} + distributed func parameterSingle(first: String) {} + distributed func parameterPair(first: String, second: Int) {} + // FIXME(distributed): rdar://90293494 fails to get +// distributed func generic(first: A) {} +// distributed func genericNoLabel(_ first: A) {} +} +extension Greeter { + distributed func parameterTriple(first: String, second: Int, third: Double) {} +} + +func test() async throws { + let system = DefaultDistributedActorSystem() + let g = Greeter(system: system) + let greeter = try Greeter.resolve(id: g.id, using: system) + + try await greeter.noParams() + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.noParams() + + _ = try await greeter.parameterSingle(first: "X") + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.parameterSingle(first:) + + try await greeter.noLabel("") + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.noLabel(_:) + + try await greeter.noLabels2("", "") + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.noLabels2(_:_:) + + try await greeter.noLabels3("", "", "") + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.noLabels3(_:_:_:) + + try await greeter.oneLabel(value: "", "", "") + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.oneLabel(value:_:_:) + + _ = try await greeter.parameterPair(first: "X", second: 2) + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.parameterPair(first:second:) + + _ = try await greeter.parameterTriple(first: "X", second: 2, third: 3.0) + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.parameterTriple(first:second:third:) + + // FIXME: rdar://90293494 seems to fail getting the substitutions? +// _ = try await greeter.generic(first: "X") +// // TODO: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.parameterTriple(first:second:third:) + + print("done") + // CHECK: done +} + +@main struct Main { + static func main() async { + try! await test() + } +} diff --git a/test/Distributed/Runtime/distributed_actor_remote_functions.swift b/test/Distributed/Runtime/distributed_actor_remote_functions.swift index 2dff787567eda..3f60926193053 100644 --- a/test/Distributed/Runtime/distributed_actor_remote_functions.swift +++ b/test/Distributed/Runtime/distributed_actor_remote_functions.swift @@ -128,11 +128,11 @@ struct FakeActorSystem: DistributedActorSystem { Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - guard target.mangledName != "$s4main28SomeSpecificDistributedActorC24helloThrowsTransportBoomSSyYaKFTE" else { + guard "\(target)" != "main.SomeSpecificDistributedActor.helloThrowsTransportBoom()" else { throw Boom("system") } - return "remote(\(target.mangledName))" as! Res + return "remote(\(target))" as! Res } func remoteCallVoid( @@ -234,18 +234,12 @@ func test_remote_invoke(address: ActorAddress, system: FakeActorSystem) async { print("remote isRemote: \(__isRemoteActor(remote))") // CHECK: remote isRemote: true await check(actor: remote) - // TODO(distributed): remote - helloAsyncThrows: remote(_remote_impl_helloAsyncThrows()) - // CHECK: remote - helloAsyncThrows: remote($s4main28SomeSpecificDistributedActorC16helloAsyncThrowsSSyYaKFTE) - // TODO(distributed): remote - helloAsync: remote() - // CHECK: remote - helloAsync: remote($s4main28SomeSpecificDistributedActorC10helloAsyncSSyYaKFTE) - // TODO(distributed): remote - helloThrows: remote(_remote_impl_helloThrows()) - // CHECK: remote - helloThrows: remote($s4main28SomeSpecificDistributedActorC11helloThrowsSSyYaKFTE) - // TODO(distributed): remote - hello: remote(_remote_impl_hello()) - // CHECK: remote - hello: remote($s4main28SomeSpecificDistributedActorC5helloSSyYaKFTE) - // TODO(distributed): remote - callTaskSelf: remote(_remote_impl_callTaskSelf()) - // CHECK: remote - callTaskSelf: remote($s4main28SomeSpecificDistributedActorC12callTaskSelfSSyYaKFTE) - // TODO(distributed): remote - callDetachedSelf: remote(_remote_impl_callDetachedSelf()) - // CHECK: remote - callDetachedSelf: remote($s4main28SomeSpecificDistributedActorC16callDetachedSelfSSyYaKFTE) + // CHECK: remote - helloAsyncThrows: remote(main.SomeSpecificDistributedActor.helloAsyncThrows()) + // CHECK: remote - helloAsync: remote(main.SomeSpecificDistributedActor.helloAsync()) + // CHECK: remote - helloThrows: remote(main.SomeSpecificDistributedActor.helloThrows()) + // CHECK: remote - hello: remote(main.SomeSpecificDistributedActor.hello()) + // CHECK: remote - callTaskSelf: remote(main.SomeSpecificDistributedActor.callTaskSelf()) + // CHECK: remote - callDetachedSelf: remote(main.SomeSpecificDistributedActor.callDetachedSelf()) // CHECK: remote - helloThrowsTransportBoom: Boom(whoFailed: "system") print(local) diff --git a/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift b/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift index 26fe5d44fe575..be10bc5e2799c 100644 --- a/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift +++ b/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift @@ -139,7 +139,7 @@ struct Error_wrongReturn: DistributedActorSystem { Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } public func remoteCall( @@ -153,7 +153,7 @@ struct Error_wrongReturn: DistributedActorSystem { Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } public func remoteCallVoid( @@ -165,7 +165,7 @@ struct Error_wrongReturn: DistributedActorSystem { where Act: DistributedActor, Act.ID == ActorID, Err: Error { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } } @@ -209,7 +209,7 @@ struct BadRemoteCall_param: DistributedActorSystem { Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } public func remoteCallVoid( @@ -221,7 +221,7 @@ struct BadRemoteCall_param: DistributedActorSystem { where Act: DistributedActor, Act.ID == ActorID, Err: Error { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } } @@ -260,7 +260,7 @@ public struct BadRemoteCall_notPublic: DistributedActorSystem { Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } // expected-error@+1{{method 'remoteCallVoid(on:target:invocation:throwing:)' must be as accessible as its enclosing type because it matches a requirement in protocol 'DistributedActorSystem'}} @@ -273,7 +273,7 @@ public struct BadRemoteCall_notPublic: DistributedActorSystem { where Act: DistributedActor, Act.ID == ActorID, Err: Error { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } } @@ -314,7 +314,7 @@ public struct BadRemoteCall_badResultConformance: DistributedActorSystem { Act.ID == ActorID, Err: Error, Res: SomeProtocol { // ERROR: bad, this must be SerializationRequirement - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } public func remoteCallVoid( @@ -326,7 +326,7 @@ public struct BadRemoteCall_badResultConformance: DistributedActorSystem { where Act: DistributedActor, Act.ID == ActorID, Err: Error { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } } @@ -366,7 +366,7 @@ struct BadRemoteCall_largeSerializationRequirement: DistributedActorSystem { Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } func remoteCallVoid( @@ -378,7 +378,7 @@ struct BadRemoteCall_largeSerializationRequirement: DistributedActorSystem { where Act: DistributedActor, Act.ID == ActorID, Err: Error { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } } @@ -419,7 +419,7 @@ struct BadRemoteCall_largeSerializationRequirementSlightlyOffInDefinition: Distr Act.ID == ActorID, Err: Error, Res: SomeProtocol { // ERROR: missing Codable!!! - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } func remoteCallVoid( @@ -431,7 +431,7 @@ struct BadRemoteCall_largeSerializationRequirementSlightlyOffInDefinition: Distr where Act: DistributedActor, Act.ID == ActorID, Err: Error { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } } @@ -469,7 +469,7 @@ struct BadRemoteCall_anySerializationRequirement: DistributedActorSystem { Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } func remoteCallVoid( @@ -481,7 +481,7 @@ struct BadRemoteCall_anySerializationRequirement: DistributedActorSystem { where Act: DistributedActor, Act.ID == ActorID, Err: Error { - throw ExecuteDistributedTargetError(message: "Not implemented.") + throw ExecuteDistributedTargetError(message: "\(#function) not implemented.") } } @@ -573,8 +573,7 @@ struct FakeInvocationEncoder_missing_recordReturnType: DistributedTargetInvocati } struct FakeInvocationEncoder_missing_recordErrorType: DistributedTargetInvocationEncoder { - //expected-error@-1{{struct 'FakeInvocationEncoder_missing_recordErrorType' is missing witness for protocol requirement 'recordErrorType'}} - //expected-note@-2{{protocol 'FakeInvocationEncoder_missing_recordErrorType' requires function 'recordErrorType' with signature:}} + //expected-error@-1{{type 'FakeInvocationEncoder_missing_recordErrorType' does not conform to protocol 'DistributedTargetInvocationEncoder'}} typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} @@ -613,8 +612,7 @@ struct FakeInvocationEncoder_recordResultType_wrongType: DistributedTargetInvoca } struct FakeInvocationEncoder_recordErrorType_wrongType: DistributedTargetInvocationEncoder { - //expected-error@-1{{struct 'FakeInvocationEncoder_recordErrorType_wrongType' is missing witness for protocol requirement 'recordErrorType'}} - //expected-note@-2{{protocol 'FakeInvocationEncoder_recordErrorType_wrongType' requires function 'recordErrorType' with signature:}} + //expected-error@-1{{type 'FakeInvocationEncoder_recordErrorType_wrongType' does not conform to protocol 'DistributedTargetInvocationEncoder'}} typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} @@ -622,6 +620,7 @@ struct FakeInvocationEncoder_recordErrorType_wrongType: DistributedTargetInvocat mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(BadName type: E.Type) throws {} // BAD mutating func recordErrorType(_ type: E.Type) throws {} // BAD + //expected-note@-1{{candidate has non-matching type ' (E.Type) throws -> ()'}} mutating func doneRecording() throws {} } From 7718448636ad3ca42646d1162f680013eaa1b148 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 15 Mar 2022 17:54:59 +0900 Subject: [PATCH 067/242] [Distributed] Simplify repr of RemoteCallTarget --- .../Distributed/DistributedActorSystem.swift | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index d57f6bead91ef..e393ac1db21e0 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -178,6 +178,12 @@ extension DistributedActorSystem { ) async throws where Act: DistributedActor, // Act.ID == ActorID, // FIXME(distributed): can we bring this back? ResultHandler: DistributedTargetInvocationResultHandler { + // NOTE: Implementation could be made more efficient because we still risk + // demangling a RemoteCallTarget identity (if it is a mangled name) multiple + // times. We would prefer to store if it is a mangled name, demangle, and + // always refer to that demangled repr perhaps? We do cache the resulting + // pretty formatted name of the call target, but perhaps we can do better. + // Get the expected parameter count of the func guard let targetName = target.identifier else { throw ExecuteDistributedTargetError( @@ -338,33 +344,24 @@ extension DistributedActorSystem { /// however its exact format is not specified and may change in future versions. @available(SwiftStdlib 5.7, *) public struct RemoteCallTarget: CustomStringConvertible { - private let _storage: _Storage - private enum _Storage { - case mangledName(String) - } + private let _identifier: String - // Only intended to be created by the _Distributed library. - // TODO(distributed): make this internal and only allow calling by the synthesized code? - public init(_mangledName: String) { - self._storage = .mangledName(_mangledName) + public init(_ identifier: String) { + self._identifier = identifier } /// The underlying identifier of the target, returned as-is. public var identifier: String? { - switch self._storage { - case .mangledName(let name): - return name - } + return _identifier } + /// Attempts to pretty format the underlying target identifier. + /// If unable to, returns the raw underlying identifier. public var description: String { - switch self._storage { - case .mangledName(let mangledName): - if let name = _getFunctionFullNameFromMangledName(mangledName: mangledName) { - return name - } else { - return "\(mangledName)" - } + if let name = _getFunctionFullNameFromMangledName(mangledName: _identifier) { + return name + } else { + return "\(_identifier)" } } } From 7a1792ab4e902008799168f3bca78f060a85aefa Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 14 Mar 2022 15:23:17 +0100 Subject: [PATCH 068/242] [Parser] Support 'any' type in SwiftSyntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, SwiftSyntax wasn’t able to parse 'any' types. Add support for them now. rdar://90077430 --- lib/Parse/ParseType.cpp | 5 +++-- test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds | 6 +++--- .../{round_trip_tuple.swift => round_trip_types.swift} | 6 ++++++ utils/gyb_syntax_support/NodeSerializationCodes.py | 2 +- utils/gyb_syntax_support/TypeNodes.py | 8 ++++---- 5 files changed, 17 insertions(+), 10 deletions(-) rename test/Syntax/{round_trip_tuple.swift => round_trip_types.swift} (75%) diff --git a/lib/Parse/ParseType.cpp b/lib/Parse/ParseType.cpp index 4ab6511514bd7..16feac79393db 100644 --- a/lib/Parse/ParseType.cpp +++ b/lib/Parse/ParseType.cpp @@ -800,7 +800,8 @@ Parser::parseTypeIdentifier(bool isParsingQualifiedDeclBaseType) { /// type-composition '&' type-simple ParserResult Parser::parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason) { - SyntaxParsingContext SomeTypeContext(SyntaxContext, SyntaxKind::SomeType); + SyntaxParsingContext ConstrainedSugarTypeContext( + SyntaxContext, SyntaxKind::ConstrainedSugarType); // Check for the opaque modifier. // This is only semantically allowed in certain contexts, but we parse it // generally for diagnostics and recovery. @@ -816,7 +817,7 @@ Parser::parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason) { anyLoc = consumeToken(); } else { // This isn't a some type. - SomeTypeContext.setTransparent(); + ConstrainedSugarTypeContext.setTransparent(); } auto applyOpaque = [&](TypeRepr *type) -> TypeRepr * { diff --git a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds index 16ea17f9feb9e..2014006e7545c 100644 --- a/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds +++ b/test/Syntax/Outputs/round_trip_parse_gen.swift.withkinds @@ -580,9 +580,9 @@ func foo() false) #assert(true, "hello world") -public func anyFoo() -> some Foo {} -public func qoo() -> some O & O2 {} -func zlop() -> some C & AnyObject & P {} +public func anyFoo() -> some Foo {} +public func qoo() -> some O & O2 {} +func zlop() -> some C & AnyObject & P {} @custom(a, b,c) func foo() {} diff --git a/test/Syntax/round_trip_tuple.swift b/test/Syntax/round_trip_types.swift similarity index 75% rename from test/Syntax/round_trip_tuple.swift rename to test/Syntax/round_trip_types.swift index cde5c2a6848a1..f8dc543e7d8b3 100644 --- a/test/Syntax/round_trip_tuple.swift +++ b/test/Syntax/round_trip_types.swift @@ -4,3 +4,9 @@ typealias TwoInts = (Int, Int) typealias TwoNamedInts = (a: Int, b: Int) typealias VoidTuple = () typealias TupleWithTrivia = ( Int , b :Int ) + +func testAnyType() { + protocol MyProto {} + + let foo: [Int: any MyProto] +} diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 468ee3093b968..675ad7d3b1a1a 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -232,7 +232,7 @@ 'NamedAttributeStringArgument': 227, 'DeclName': 228, 'PoundAssertStmt': 229, - 'SomeType': 230, + 'ConstrainedSugarType': 230, 'CustomAttribute': 231, 'GenericRequirement': 232, 'DifferentiableAttributeArguments': 233, diff --git a/utils/gyb_syntax_support/TypeNodes.py b/utils/gyb_syntax_support/TypeNodes.py index 527dc4d3ccd40..748a47bcf2ebb 100644 --- a/utils/gyb_syntax_support/TypeNodes.py +++ b/utils/gyb_syntax_support/TypeNodes.py @@ -77,12 +77,12 @@ Child('QuestionMark', kind='PostfixQuestionMarkToken'), ]), - # some type -> some 'type' - Node('SomeType', kind='Type', + # constrained-sugar-type -> ('some'|'any') type + Node('ConstrainedSugarType', kind='Type', children=[ - Child('SomeSpecifier', kind='IdentifierToken', + Child('SomeOrAnySpecifier', kind='IdentifierToken', classification='Keyword', - text_choices=['some']), + text_choices=['some', 'any']), Child('BaseType', kind='Type'), ]), From 6caeecdc586175d553e57fec36e4d4d72bb7541b Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 15 Mar 2022 23:41:03 +0900 Subject: [PATCH 069/242] fix locating constructor --- lib/Sema/TypeCheckDistributed.cpp | 4 +-- .../Distributed/DistributedActorSystem.swift | 7 ++--- .../distributed_actor_remoteCall.swift | 26 +++++++++---------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp index f8bec3cdf3df0..35c476ed39344 100644 --- a/lib/Sema/TypeCheckDistributed.cpp +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -721,13 +721,13 @@ GetDistributedRemoteCallTargetInitFunctionRequest::evaluate( if (params->size() != 1) return nullptr; - if (params->get(0)->getArgumentName() == C.getIdentifier("_mangledName")) + // _ identifier + if (params->get(0)->getArgumentName().empty()) return ctor; return nullptr; } - // TODO(distributed): make a Request for it? return nullptr; } diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index e393ac1db21e0..841c132b9d234 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -185,10 +185,7 @@ extension DistributedActorSystem { // pretty formatted name of the call target, but perhaps we can do better. // Get the expected parameter count of the func - guard let targetName = target.identifier else { - throw ExecuteDistributedTargetError( - message: "Unable to extract target identifier from remote call target: \(target)") - } + let targetName = target.identifier let nameUTF8 = Array(targetName.utf8) // Gen the generic environment (if any) associated with the target. @@ -351,7 +348,7 @@ public struct RemoteCallTarget: CustomStringConvertible { } /// The underlying identifier of the target, returned as-is. - public var identifier: String? { + public var identifier: String { return _identifier } diff --git a/test/Distributed/Runtime/distributed_actor_remoteCall.swift b/test/Distributed/Runtime/distributed_actor_remoteCall.swift index e156d9463f07c..74bde0ec1f61d 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCall.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCall.swift @@ -304,7 +304,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: emptyName), + target: RemoteCallTarget(emptyName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -312,7 +312,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: helloName), + target: RemoteCallTarget(helloName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -320,7 +320,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: answerName), + target: RemoteCallTarget(answerName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -328,7 +328,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: largeResultName), + target: RemoteCallTarget(largeResultName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -336,7 +336,7 @@ func test() async throws { try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: enumResultName), + target: RemoteCallTarget(enumResultName), invocationDecoder: &emptyInvocation, handler: FakeResultHandler() ) @@ -350,7 +350,7 @@ func test() async throws { var echoDecoder = echoInvocation.makeDecoder() try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: echoName), + target: RemoteCallTarget(echoName), invocationDecoder: &echoDecoder, handler: FakeResultHandler() ) @@ -365,7 +365,7 @@ func test() async throws { var generic1Decoder = generic1Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: generic1Name), + target: RemoteCallTarget(generic1Name), invocationDecoder: &generic1Decoder, handler: FakeResultHandler() ) @@ -383,7 +383,7 @@ func test() async throws { var generic2Decoder = generic2Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: generic2Name), + target: RemoteCallTarget(generic2Name), invocationDecoder: &generic2Decoder, handler: FakeResultHandler() ) @@ -404,7 +404,7 @@ func test() async throws { var generic3Decoder = generic3Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: generic3Name), + target: RemoteCallTarget(generic3Name), invocationDecoder: &generic3Decoder, handler: FakeResultHandler() ) @@ -426,7 +426,7 @@ func test() async throws { var generic4Decoder = generic4Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: generic4Name), + target: RemoteCallTarget(generic4Name), invocationDecoder: &generic4Decoder, handler: FakeResultHandler() ) @@ -450,7 +450,7 @@ func test() async throws { var generic5Decoder = generic5Invocation.makeDecoder() try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: generic5Name), + target: RemoteCallTarget(generic5Name), invocationDecoder: &generic5Decoder, handler: FakeResultHandler() ) @@ -473,7 +473,7 @@ func test() async throws { var genericOptDecoder = genericOptInvocation.makeDecoder() try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: genericOptionalName), + target: RemoteCallTarget(genericOptionalName), invocationDecoder: &genericOptDecoder, handler: FakeResultHandler() ) @@ -488,7 +488,7 @@ func test() async throws { var decodeErrDecoder = decodeErrInvocation.makeDecoder() try await system.executeDistributedTarget( on: local, - target: RemoteCallTarget(_mangledName: expectsDecodeErrorName), + target: RemoteCallTarget(expectsDecodeErrorName), invocationDecoder: &decodeErrDecoder, handler: FakeResultHandler() ) From 755941c21fa8a3c27b60be4b697d62f443bce142 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sat, 12 Mar 2022 23:14:39 -0500 Subject: [PATCH 070/242] RequirementMachine: Split off Rule.cpp from RewriteSystem.cpp --- lib/AST/CMakeLists.txt | 1 + lib/AST/RequirementMachine/RewriteSystem.cpp | 271 +---------------- lib/AST/RequirementMachine/RewriteSystem.h | 181 +----------- lib/AST/RequirementMachine/Rule.cpp | 289 +++++++++++++++++++ lib/AST/RequirementMachine/Rule.h | 212 ++++++++++++++ lib/AST/RequirementMachine/Symbol.h | 1 + 6 files changed, 507 insertions(+), 448 deletions(-) create mode 100644 lib/AST/RequirementMachine/Rule.cpp create mode 100644 lib/AST/RequirementMachine/Rule.h diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index d23b1952f7d1b..d56782623394c 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -94,6 +94,7 @@ add_swift_host_library(swiftAST STATIC RequirementMachine/RewriteContext.cpp RequirementMachine/RewriteLoop.cpp RequirementMachine/RewriteSystem.cpp + RequirementMachine/Rule.cpp RequirementMachine/SimplifySubstitutions.cpp RequirementMachine/Symbol.cpp RequirementMachine/Term.cpp diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp index 55b00ff165478..bad0a7c6027ad 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.cpp +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -12,283 +12,18 @@ #include "swift/AST/Decl.h" #include "swift/AST/Types.h" -#include "swift/AST/TypeWalker.h" -#include "llvm/ADT/FoldingSet.h" #include "llvm/Support/raw_ostream.h" #include #include #include "RewriteContext.h" +#include "RewriteLoop.h" #include "RewriteSystem.h" +#include "Rule.h" +#include "Trie.h" using namespace swift; using namespace rewriting; -/// If this is a rule of the form T.[p] => T where [p] is a property symbol, -/// returns the symbol. Otherwise, returns None. -/// -/// Note that this is meant to be used with a simplified rewrite system, -/// where the right hand sides of rules are canonical, since this also means -/// that T is canonical. -Optional Rule::isPropertyRule() const { - auto property = LHS.back(); - - if (!property.isProperty()) - return None; - - if (LHS.size() - 1 != RHS.size()) - return None; - - if (!std::equal(RHS.begin(), RHS.end(), LHS.begin())) - return None; - - return property; -} - -/// If this is a rule of the form T.[P] => T where [P] is a protocol symbol, -/// return the protocol P, otherwise return nullptr. -const ProtocolDecl *Rule::isProtocolConformanceRule() const { - if (auto property = isPropertyRule()) { - if (property->getKind() == Symbol::Kind::Protocol) - return property->getProtocol(); - } - - return nullptr; -} - -/// If this is a rule of the form T.[concrete: C : P] => T where -/// [concrete: C : P] is a concrete conformance symbol, return the protocol P, -/// otherwise return nullptr. -const ProtocolDecl *Rule::isAnyConformanceRule() const { - if (auto property = isPropertyRule()) { - switch (property->getKind()) { - case Symbol::Kind::ConcreteConformance: - case Symbol::Kind::Protocol: - return property->getProtocol(); - - case Symbol::Kind::Layout: - case Symbol::Kind::Superclass: - case Symbol::Kind::ConcreteType: - return nullptr; - - case Symbol::Kind::Name: - case Symbol::Kind::AssociatedType: - case Symbol::Kind::GenericParam: - break; - } - - llvm_unreachable("Bad symbol kind"); - } - - return nullptr; -} - -/// If this is a rule of the form [P].[P] => [P] where [P] is a protocol -/// symbol, return true, otherwise return false. -bool Rule::isIdentityConformanceRule() const { - return (LHS.size() == 2 && - RHS.size() == 1 && - LHS[0] == RHS[0] && - LHS[0] == LHS[1] && - LHS[0].getKind() == Symbol::Kind::Protocol); -} - -/// If this is a rule of the form [P].[Q] => [P] where [P] and [Q] are -/// protocol symbols, return true, otherwise return false. -bool Rule::isProtocolRefinementRule() const { - if (LHS.size() == 2 && - RHS.size() == 1 && - LHS[0] == RHS[0] && - LHS[0].getKind() == Symbol::Kind::Protocol && - (LHS[1].getKind() == Symbol::Kind::Protocol || - LHS[1].getKind() == Symbol::Kind::ConcreteConformance) && - LHS[0] != LHS[1]) { - - // A protocol refinement rule must be from a directly-stated - // inheritance clause entry. It can only become redundant if it is - // written in terms of other protocol refinement rules; otherwise, it - // must appear in the protocol's requirement signature. - // - // See RewriteSystem::isValidRefinementPath() for an explanation. - auto *proto = LHS[0].getProtocol(); - auto *otherProto = LHS[1].getProtocol(); - - auto inherited = proto->getInheritedProtocols(); - return (std::find(inherited.begin(), inherited.end(), otherProto) - != inherited.end()); - } - - return false; -} - -/// If this is a rule of the form [P].[concrete: C : Q] => [P] where -/// [P] is a protocol symbol, return true. -/// -/// This means that P constrains 'Self' to a concrete type that conforms -/// to some Q with P : Q. We don't consider this to be a valid conformance -/// path element, to ensure compatibility with the GSB in an odd edge -/// case: -/// -/// protocol P : C {} -/// class C : P {} -/// -/// The GSB minimizes the signature to , -/// whereas the minimal conformances algorithm would otherwise minimize -/// it down to on account of the (T.[P] => T) conformance -/// rule being redundantly expressed via [P].[concrete: C : P]. -bool Rule::isCircularConformanceRule() const { - if (LHS.size() != 2 || RHS.size() != 1 || LHS[0] != RHS[0]) - return false; - - if (LHS[0].getKind() != Symbol::Kind::Protocol || - LHS[1].getKind() != Symbol::Kind::ConcreteConformance) - return false; - - return true; -} - -/// A protocol typealias rule takes one of the following two forms, -/// where T is a name symbol: -/// -/// 1) [P].T => X -/// 2) [P].T.[concrete: C] => [P].T -/// -/// The first case is where the protocol's underlying type is another -/// type parameter. The second case is where the protocol's underlying -/// type is a concrete type. -/// -/// In the first case, X must be fully resolved, that is, it must not -/// contain any name symbols. -/// -/// If this rule is a protocol typealias rule, returns its name. Otherwise -/// returns None. -Optional Rule::isProtocolTypeAliasRule() const { - if (LHS.size() != 2 && LHS.size() != 3) - return None; - - if (LHS[0].getKind() != Symbol::Kind::Protocol || - LHS[1].getKind() != Symbol::Kind::Name) - return None; - - if (LHS.size() == 2) { - // This is the case where the underlying type is a type parameter. - // - // We shouldn't have unresolved symbols on the right hand side; - // they should have been simplified away. - if (RHS.containsUnresolvedSymbols()) { - if (RHS.size() != 2 || - RHS[0] != LHS[0] || - RHS[1].getKind() != Symbol::Kind::Name) { - return None; - } - } - } else { - // This is the case where the underlying type is concrete. - assert(LHS.size() == 3); - - auto prop = isPropertyRule(); - if (!prop || prop->getKind() != Symbol::Kind::ConcreteType) - return None; - } - - return LHS[1].getName(); -} - -/// A rule was derived from a concrete protocol typealias if it -/// takes the following form: -/// -/// T.A.[concrete: C] => T.A -/// -/// Where the prefix term T does not contain any name symbols, and -/// A is a name symbol. -bool Rule::isDerivedFromConcreteProtocolTypeAliasRule() const { - auto optSymbol = isPropertyRule(); - if (!optSymbol || optSymbol->getKind() != Symbol::Kind::ConcreteType) - return false; - - for (unsigned i = 0, e = RHS.size() - 1; i < e; ++i) { - if (RHS[i].getKind() == Symbol::Kind::Name) - return false; - } - - if (RHS.back().getKind() != Symbol::Kind::Name) - return false; - - return true; -} - -/// Returns the length of the left hand side. -unsigned Rule::getDepth() const { - auto result = LHS.size(); - - if (LHS.back().hasSubstitutions()) { - for (auto substitution : LHS.back().getSubstitutions()) { - result = std::max(result, substitution.size()); - } - } - - return result; -} - -/// Returns the nesting depth of the concrete symbol at the end of the -/// left hand side, or 0 if there isn't one. -unsigned Rule::getNesting() const { - if (LHS.back().hasSubstitutions()) { - auto type = LHS.back().getConcreteType(); - - struct Walker : TypeWalker { - unsigned Nesting = 0; - unsigned MaxNesting = 0; - - Action walkToTypePre(Type ty) override { - ++Nesting; - MaxNesting = std::max(Nesting, MaxNesting); - - return Action::Continue; - } - - Action walkToTypePost(Type ty) override { - --Nesting; - - return Action::Continue; - } - }; - - Walker walker; - type.walk(walker); - - return walker.MaxNesting; - } - - return 0; -} - -/// Linear order on rules; compares LHS followed by RHS. -Optional Rule::compare(const Rule &other, RewriteContext &ctx) const { - Optional compare = LHS.compare(other.LHS, ctx); - if (!compare.hasValue() || *compare != 0) - return compare; - - return RHS.compare(other.RHS, ctx); -} - -void Rule::dump(llvm::raw_ostream &out) const { - out << LHS << " => " << RHS; - if (Permanent) - out << " [permanent]"; - if (Explicit) - out << " [explicit]"; - if (LHSSimplified) - out << " [lhs↓]"; - if (RHSSimplified) - out << " [rhs↓]"; - if (SubstitutionSimplified) - out << " [subst↓]"; - if (Redundant) - out << " [redundant]"; - if (Conflicting) - out << " [conflicting]"; -} - RewriteSystem::RewriteSystem(RewriteContext &ctx) : Context(ctx), Debug(ctx.getDebugOptions()) { Initialized = 0; diff --git a/lib/AST/RequirementMachine/RewriteSystem.h b/lib/AST/RequirementMachine/RewriteSystem.h index 07d0822706e8e..42e5303edb3d6 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.h +++ b/lib/AST/RequirementMachine/RewriteSystem.h @@ -16,11 +16,11 @@ #include "swift/AST/Requirement.h" #include "swift/AST/TypeCheckRequests.h" #include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/PointerUnion.h" #include "Debug.h" #include "Diagnostics.h" #include "RewriteLoop.h" +#include "Rule.h" #include "Symbol.h" #include "Term.h" #include "Trie.h" @@ -38,185 +38,6 @@ class PropertyMap; class RewriteContext; class RewriteSystem; -/// A rewrite rule that replaces occurrences of LHS with RHS. -/// -/// LHS must be greater than RHS in the linear order over terms. -/// -/// Out-of-line methods are documented in RewriteSystem.cpp. -class Rule final { - Term LHS; - Term RHS; - - /// The written requirement ID, which can be used to index into the - /// \c WrittenRequirements array in the rewrite system to retrieve - /// the structural requirement. - Optional requirementID; - - /// A 'permanent' rule cannot be deleted by homotopy reduction. These - /// do not correspond to generic requirements and are re-added when the - /// rewrite system is built. - unsigned Permanent : 1; - - /// An 'explicit' rule is a generic requirement written by the user. - unsigned Explicit : 1; - - /// An 'LHS simplified' rule's left hand side was reduced via another rule. - /// Set by simplifyLeftHandSides(). - unsigned LHSSimplified : 1; - - /// An 'RHS simplified' rule's right hand side can be reduced via another rule. - /// Set by simplifyRightHandSides(). - unsigned RHSSimplified : 1; - - /// A 'substitution simplified' rule's left hand side contains substitutions - /// which can be reduced via another rule. - /// Set by simplifyLeftHandSideSubstitutions(). - unsigned SubstitutionSimplified : 1; - - /// A 'redundant' rule was eliminated by homotopy reduction. Redundant rules - /// still participate in term rewriting, but they are not part of the minimal - /// set of requirements in a generic signature. - unsigned Redundant : 1; - - /// A 'conflicting' rule is a property rule which cannot be satisfied by any - /// concrete type because it is mutually exclusive with some other rule. - /// An example would be a pair of concrete type rules: - /// - /// T.[concrete: Int] => T - /// T.[concrete: String] => T - /// - /// Conflicting rules are detected in property map construction, and are - /// dropped from the minimal set of requirements. - unsigned Conflicting : 1; - -public: - Rule(Term lhs, Term rhs) - : LHS(lhs), RHS(rhs) { - Permanent = false; - Explicit = false; - LHSSimplified = false; - RHSSimplified = false; - SubstitutionSimplified = false; - Redundant = false; - Conflicting = false; - } - - const Term &getLHS() const { return LHS; } - const Term &getRHS() const { return RHS; } - - Optional getRequirementID() const { - return requirementID; - } - - void setRequirementID(Optional requirementID) { - assert(!getRequirementID().hasValue()); - this->requirementID = requirementID; - } - - Optional isPropertyRule() const; - - const ProtocolDecl *isProtocolConformanceRule() const; - - const ProtocolDecl *isAnyConformanceRule() const; - - bool isIdentityConformanceRule() const; - - bool isProtocolRefinementRule() const; - - bool isCircularConformanceRule() const; - - /// See above for an explanation of these predicates. - bool isPermanent() const { - return Permanent; - } - - bool isExplicit() const { - return Explicit; - } - - bool isLHSSimplified() const { - return LHSSimplified; - } - - bool isRHSSimplified() const { - return RHSSimplified; - } - - bool isSubstitutionSimplified() const { - return SubstitutionSimplified; - } - - bool isRedundant() const { - return Redundant; - } - - bool isConflicting() const { - return Conflicting; - } - - bool containsUnresolvedSymbols() const { - return (LHS.containsUnresolvedSymbols() || - RHS.containsUnresolvedSymbols()); - } - - Optional isProtocolTypeAliasRule() const; - - bool isDerivedFromConcreteProtocolTypeAliasRule() const; - - void markLHSSimplified() { - assert(!LHSSimplified); - LHSSimplified = true; - } - - void markRHSSimplified() { - assert(!RHSSimplified); - RHSSimplified = true; - } - - void markSubstitutionSimplified() { - assert(!SubstitutionSimplified); - SubstitutionSimplified = true; - } - - void markPermanent() { - assert(!Explicit && !Permanent && - "Permanent and explicit are mutually exclusive"); - Permanent = true; - } - - void markExplicit() { - assert(!Explicit && !Permanent && - "Permanent and explicit are mutually exclusive"); - Explicit = true; - } - - void markRedundant() { - assert(!Redundant); - Redundant = true; - } - - void markConflicting() { - // It's okay to mark a rule as conflicting multiple times, but it must not - // be a permanent rule. - assert(!Permanent && "Permanent rule should not conflict with anything"); - Conflicting = true; - } - - unsigned getDepth() const; - - unsigned getNesting() const; - - Optional compare(const Rule &other, RewriteContext &ctx) const; - - void dump(llvm::raw_ostream &out) const; - - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &out, - const Rule &rule) { - rule.dump(out); - return out; - } -}; - /// Result type for RequirementMachine::computeCompletion(). enum class CompletionResult { /// Completion was successful. diff --git a/lib/AST/RequirementMachine/Rule.cpp b/lib/AST/RequirementMachine/Rule.cpp new file mode 100644 index 0000000000000..e7dd0e7b89aba --- /dev/null +++ b/lib/AST/RequirementMachine/Rule.cpp @@ -0,0 +1,289 @@ +//===--- Rule.cpp - An oriented rewrite rule in a rewrite system ----------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "Rule.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Types.h" +#include "swift/AST/TypeWalker.h" +#include "llvm/Support/raw_ostream.h" +#include "RewriteContext.h" +#include "Term.h" +#include "Symbol.h" + +using namespace swift; +using namespace rewriting; + +/// If this is a rule of the form T.[p] => T where [p] is a property symbol, +/// returns the symbol. Otherwise, returns None. +/// +/// Note that this is meant to be used with a simplified rewrite system, +/// where the right hand sides of rules are canonical, since this also means +/// that T is canonical. +Optional Rule::isPropertyRule() const { + auto property = LHS.back(); + + if (!property.isProperty()) + return None; + + if (LHS.size() - 1 != RHS.size()) + return None; + + if (!std::equal(RHS.begin(), RHS.end(), LHS.begin())) + return None; + + return property; +} + +/// If this is a rule of the form T.[P] => T where [P] is a protocol symbol, +/// return the protocol P, otherwise return nullptr. +const ProtocolDecl *Rule::isProtocolConformanceRule() const { + if (auto property = isPropertyRule()) { + if (property->getKind() == Symbol::Kind::Protocol) + return property->getProtocol(); + } + + return nullptr; +} + +/// If this is a rule of the form T.[concrete: C : P] => T where +/// [concrete: C : P] is a concrete conformance symbol, return the protocol P, +/// otherwise return nullptr. +const ProtocolDecl *Rule::isAnyConformanceRule() const { + if (auto property = isPropertyRule()) { + switch (property->getKind()) { + case Symbol::Kind::ConcreteConformance: + case Symbol::Kind::Protocol: + return property->getProtocol(); + + case Symbol::Kind::Layout: + case Symbol::Kind::Superclass: + case Symbol::Kind::ConcreteType: + return nullptr; + + case Symbol::Kind::Name: + case Symbol::Kind::AssociatedType: + case Symbol::Kind::GenericParam: + break; + } + + llvm_unreachable("Bad symbol kind"); + } + + return nullptr; +} + +/// If this is a rule of the form [P].[P] => [P] where [P] is a protocol +/// symbol, return true, otherwise return false. +bool Rule::isIdentityConformanceRule() const { + return (LHS.size() == 2 && + RHS.size() == 1 && + LHS[0] == RHS[0] && + LHS[0] == LHS[1] && + LHS[0].getKind() == Symbol::Kind::Protocol); +} + +/// If this is a rule of the form [P].[Q] => [P] where [P] and [Q] are +/// protocol symbols, return true, otherwise return false. +bool Rule::isProtocolRefinementRule() const { + if (LHS.size() == 2 && + RHS.size() == 1 && + LHS[0] == RHS[0] && + LHS[0].getKind() == Symbol::Kind::Protocol && + (LHS[1].getKind() == Symbol::Kind::Protocol || + LHS[1].getKind() == Symbol::Kind::ConcreteConformance) && + LHS[0] != LHS[1]) { + + // A protocol refinement rule must be from a directly-stated + // inheritance clause entry. It can only become redundant if it is + // written in terms of other protocol refinement rules; otherwise, it + // must appear in the protocol's requirement signature. + // + // See RewriteSystem::isValidRefinementPath() for an explanation. + auto *proto = LHS[0].getProtocol(); + auto *otherProto = LHS[1].getProtocol(); + + auto inherited = proto->getInheritedProtocols(); + return (std::find(inherited.begin(), inherited.end(), otherProto) + != inherited.end()); + } + + return false; +} + +/// If this is a rule of the form [P].[concrete: C : Q] => [P] where +/// [P] is a protocol symbol, return true. +/// +/// This means that P constrains 'Self' to a concrete type that conforms +/// to some Q with P : Q. We don't consider this to be a valid conformance +/// path element, to ensure compatibility with the GSB in an odd edge +/// case: +/// +/// protocol P : C {} +/// class C : P {} +/// +/// The GSB minimizes the signature to , +/// whereas the minimal conformances algorithm would otherwise minimize +/// it down to on account of the (T.[P] => T) conformance +/// rule being redundantly expressed via [P].[concrete: C : P]. +bool Rule::isCircularConformanceRule() const { + if (LHS.size() != 2 || RHS.size() != 1 || LHS[0] != RHS[0]) + return false; + + if (LHS[0].getKind() != Symbol::Kind::Protocol || + LHS[1].getKind() != Symbol::Kind::ConcreteConformance) + return false; + + return true; +} + +/// A protocol typealias rule takes one of the following two forms, +/// where T is a name symbol: +/// +/// 1) [P].T => X +/// 2) [P].T.[concrete: C] => [P].T +/// +/// The first case is where the protocol's underlying type is another +/// type parameter. The second case is where the protocol's underlying +/// type is a concrete type. +/// +/// In the first case, X must be fully resolved, that is, it must not +/// contain any name symbols. +/// +/// If this rule is a protocol typealias rule, returns its name. Otherwise +/// returns None. +Optional Rule::isProtocolTypeAliasRule() const { + if (LHS.size() != 2 && LHS.size() != 3) + return None; + + if (LHS[0].getKind() != Symbol::Kind::Protocol || + LHS[1].getKind() != Symbol::Kind::Name) + return None; + + if (LHS.size() == 2) { + // This is the case where the underlying type is a type parameter. + // + // We shouldn't have unresolved symbols on the right hand side; + // they should have been simplified away. + if (RHS.containsUnresolvedSymbols()) { + if (RHS.size() != 2 || + RHS[0] != LHS[0] || + RHS[1].getKind() != Symbol::Kind::Name) { + return None; + } + } + } else { + // This is the case where the underlying type is concrete. + assert(LHS.size() == 3); + + auto prop = isPropertyRule(); + if (!prop || prop->getKind() != Symbol::Kind::ConcreteType) + return None; + } + + return LHS[1].getName(); +} + +/// A rule was derived from a concrete protocol typealias if it +/// takes the following form: +/// +/// T.A.[concrete: C] => T.A +/// +/// Where the prefix term T does not contain any name symbols, and +/// A is a name symbol. +bool Rule::isDerivedFromConcreteProtocolTypeAliasRule() const { + auto optSymbol = isPropertyRule(); + if (!optSymbol || optSymbol->getKind() != Symbol::Kind::ConcreteType) + return false; + + for (unsigned i = 0, e = RHS.size() - 1; i < e; ++i) { + if (RHS[i].getKind() == Symbol::Kind::Name) + return false; + } + + if (RHS.back().getKind() != Symbol::Kind::Name) + return false; + + return true; +} + +/// Returns the length of the left hand side. +unsigned Rule::getDepth() const { + auto result = LHS.size(); + + if (LHS.back().hasSubstitutions()) { + for (auto substitution : LHS.back().getSubstitutions()) { + result = std::max(result, substitution.size()); + } + } + + return result; +} + +/// Returns the nesting depth of the concrete symbol at the end of the +/// left hand side, or 0 if there isn't one. +unsigned Rule::getNesting() const { + if (LHS.back().hasSubstitutions()) { + auto type = LHS.back().getConcreteType(); + + struct Walker : TypeWalker { + unsigned Nesting = 0; + unsigned MaxNesting = 0; + + Action walkToTypePre(Type ty) override { + ++Nesting; + MaxNesting = std::max(Nesting, MaxNesting); + + return Action::Continue; + } + + Action walkToTypePost(Type ty) override { + --Nesting; + + return Action::Continue; + } + }; + + Walker walker; + type.walk(walker); + + return walker.MaxNesting; + } + + return 0; +} + +/// Linear order on rules; compares LHS followed by RHS. +Optional Rule::compare(const Rule &other, RewriteContext &ctx) const { + Optional compare = LHS.compare(other.LHS, ctx); + if (!compare.hasValue() || *compare != 0) + return compare; + + return RHS.compare(other.RHS, ctx); +} + +void Rule::dump(llvm::raw_ostream &out) const { + out << LHS << " => " << RHS; + if (Permanent) + out << " [permanent]"; + if (Explicit) + out << " [explicit]"; + if (LHSSimplified) + out << " [lhs↓]"; + if (RHSSimplified) + out << " [rhs↓]"; + if (SubstitutionSimplified) + out << " [subst↓]"; + if (Redundant) + out << " [redundant]"; + if (Conflicting) + out << " [conflicting]"; +} diff --git a/lib/AST/RequirementMachine/Rule.h b/lib/AST/RequirementMachine/Rule.h new file mode 100644 index 0000000000000..193264c3fb479 --- /dev/null +++ b/lib/AST/RequirementMachine/Rule.h @@ -0,0 +1,212 @@ +//===--- Rule.h - An oriented rewrite rule in a rewrite system --*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RULE_H +#define SWIFT_RULE_H + +#include "Symbol.h" +#include "Term.h" + +namespace llvm { + class raw_ostream; +} + +namespace swift { + +namespace rewriting { + +class RewriteContext; + +/// A rewrite rule that replaces occurrences of LHS with RHS. +/// +/// LHS must be greater than RHS in the linear order over terms. +/// +/// Out-of-line methods are documented in Rule.cpp. +class Rule final { + Term LHS; + Term RHS; + + /// The written requirement ID, which can be used to index into the + /// \c WrittenRequirements array in the rewrite system to retrieve + /// the structural requirement. + Optional requirementID; + + /// A 'permanent' rule cannot be deleted by homotopy reduction. These + /// do not correspond to generic requirements and are re-added when the + /// rewrite system is built. + unsigned Permanent : 1; + + /// An 'explicit' rule is a generic requirement written by the user. + unsigned Explicit : 1; + + /// An 'LHS simplified' rule's left hand side was reduced via another rule. + /// Set by simplifyLeftHandSides(). + unsigned LHSSimplified : 1; + + /// An 'RHS simplified' rule's right hand side can be reduced via another rule. + /// Set by simplifyRightHandSides(). + unsigned RHSSimplified : 1; + + /// A 'substitution simplified' rule's left hand side contains substitutions + /// which can be reduced via another rule. + /// Set by simplifyLeftHandSideSubstitutions(). + unsigned SubstitutionSimplified : 1; + + /// A 'redundant' rule was eliminated by homotopy reduction. Redundant rules + /// still participate in term rewriting, but they are not part of the minimal + /// set of requirements in a generic signature. + unsigned Redundant : 1; + + /// A 'conflicting' rule is a property rule which cannot be satisfied by any + /// concrete type because it is mutually exclusive with some other rule. + /// An example would be a pair of concrete type rules: + /// + /// T.[concrete: Int] => T + /// T.[concrete: String] => T + /// + /// Conflicting rules are detected in property map construction, and are + /// dropped from the minimal set of requirements. + unsigned Conflicting : 1; + +public: + Rule(Term lhs, Term rhs) + : LHS(lhs), RHS(rhs) { + Permanent = false; + Explicit = false; + LHSSimplified = false; + RHSSimplified = false; + SubstitutionSimplified = false; + Redundant = false; + Conflicting = false; + } + + const Term &getLHS() const { return LHS; } + const Term &getRHS() const { return RHS; } + + Optional getRequirementID() const { + return requirementID; + } + + void setRequirementID(Optional requirementID) { + assert(!getRequirementID().hasValue()); + this->requirementID = requirementID; + } + + Optional isPropertyRule() const; + + const ProtocolDecl *isProtocolConformanceRule() const; + + const ProtocolDecl *isAnyConformanceRule() const; + + bool isIdentityConformanceRule() const; + + bool isProtocolRefinementRule() const; + + bool isCircularConformanceRule() const; + + /// See above for an explanation of these predicates. + bool isPermanent() const { + return Permanent; + } + + bool isExplicit() const { + return Explicit; + } + + bool isLHSSimplified() const { + return LHSSimplified; + } + + bool isRHSSimplified() const { + return RHSSimplified; + } + + bool isSubstitutionSimplified() const { + return SubstitutionSimplified; + } + + bool isRedundant() const { + return Redundant; + } + + bool isConflicting() const { + return Conflicting; + } + + bool containsUnresolvedSymbols() const { + return (LHS.containsUnresolvedSymbols() || + RHS.containsUnresolvedSymbols()); + } + + Optional isProtocolTypeAliasRule() const; + + bool isDerivedFromConcreteProtocolTypeAliasRule() const; + + void markLHSSimplified() { + assert(!LHSSimplified); + LHSSimplified = true; + } + + void markRHSSimplified() { + assert(!RHSSimplified); + RHSSimplified = true; + } + + void markSubstitutionSimplified() { + assert(!SubstitutionSimplified); + SubstitutionSimplified = true; + } + + void markPermanent() { + assert(!Explicit && !Permanent && + "Permanent and explicit are mutually exclusive"); + Permanent = true; + } + + void markExplicit() { + assert(!Explicit && !Permanent && + "Permanent and explicit are mutually exclusive"); + Explicit = true; + } + + void markRedundant() { + assert(!Redundant); + Redundant = true; + } + + void markConflicting() { + // It's okay to mark a rule as conflicting multiple times, but it must not + // be a permanent rule. + assert(!Permanent && "Permanent rule should not conflict with anything"); + Conflicting = true; + } + + unsigned getDepth() const; + + unsigned getNesting() const; + + Optional compare(const Rule &other, RewriteContext &ctx) const; + + void dump(llvm::raw_ostream &out) const; + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &out, + const Rule &rule) { + rule.dump(out); + return out; + } +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif diff --git a/lib/AST/RequirementMachine/Symbol.h b/lib/AST/RequirementMachine/Symbol.h index 4b88688221a7a..ba77079684687 100644 --- a/lib/AST/RequirementMachine/Symbol.h +++ b/lib/AST/RequirementMachine/Symbol.h @@ -12,6 +12,7 @@ #include "swift/Basic/LLVM.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" #ifndef SWIFT_RQM_SYMBOL_H From d4732f5a1941781166ff8e388166af1a6def53df Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Sat, 12 Mar 2022 23:22:13 -0500 Subject: [PATCH 071/242] RequirementMachine: Move RewritePath and RewriteLoop methods from HomotopyReduction.cpp to RewriteLoop.cpp --- .../RequirementMachine/HomotopyReduction.cpp | 295 ------------------ lib/AST/RequirementMachine/RewriteLoop.cpp | 295 ++++++++++++++++++ 2 files changed, 295 insertions(+), 295 deletions(-) diff --git a/lib/AST/RequirementMachine/HomotopyReduction.cpp b/lib/AST/RequirementMachine/HomotopyReduction.cpp index d149e24f66c2d..81f6dd47db97a 100644 --- a/lib/AST/RequirementMachine/HomotopyReduction.cpp +++ b/lib/AST/RequirementMachine/HomotopyReduction.cpp @@ -65,93 +65,6 @@ using namespace swift; using namespace rewriting; -/// Recompute various cached values if needed. -void RewriteLoop::recompute(const RewriteSystem &system) { - if (!Dirty) - return; - Dirty = 0; - - Useful = 0; - ProjectionCount = 0; - DecomposeCount = 0; - HasConcreteTypeAliasRule = 0; - - RewritePathEvaluator evaluator(Basepoint); - for (auto step : Path) { - switch (step.Kind) { - case RewriteStep::Rule: { - Useful |= (!step.isInContext() && !evaluator.isInContext()); - - const auto &rule = system.getRule(step.getRuleID()); - if (rule.isDerivedFromConcreteProtocolTypeAliasRule()) - HasConcreteTypeAliasRule = 1; - - break; - } - - case RewriteStep::LeftConcreteProjection: - ++ProjectionCount; - break; - - case RewriteStep::Decompose: - ++DecomposeCount; - break; - - case RewriteStep::PrefixSubstitutions: - case RewriteStep::Shift: - case RewriteStep::Relation: - case RewriteStep::DecomposeConcrete: - case RewriteStep::RightConcreteProjection: - break; - } - - evaluator.apply(step, system); - } - - RulesInEmptyContext = Path.getRulesInEmptyContext(Basepoint, system); -} - -/// A rewrite rule is redundant if it appears exactly once in a loop -/// without context. -ArrayRef -RewriteLoop::findRulesAppearingOnceInEmptyContext( - const RewriteSystem &system) const { - const_cast(this)->recompute(system); - return RulesInEmptyContext; -} - -/// The number of LeftConcreteProjection steps, used by the elimination order to -/// prioritize loops that are not concrete unification projections. -unsigned RewriteLoop::getProjectionCount( - const RewriteSystem &system) const { - const_cast(this)->recompute(system); - return ProjectionCount; -} - -/// The number of Decompose steps, used by the elimination order to prioritize -/// loops that are not concrete simplifications. -unsigned RewriteLoop::getDecomposeCount( - const RewriteSystem &system) const { - const_cast(this)->recompute(system); - return DecomposeCount; -} - -/// Returns true if the loop contains at least one concrete protocol typealias rule, -/// which have the form ([P].A.[concrete: C] => [P].A). -bool RewriteLoop::hasConcreteTypeAliasRule( - const RewriteSystem &system) const { - const_cast(this)->recompute(system); - return HasConcreteTypeAliasRule; -} - -/// The number of Decompose steps, used by the elimination order to prioritize -/// loops that are not concrete simplifications. -bool RewriteLoop::isUseful( - const RewriteSystem &system) const { - const_cast(this)->recompute(system); - return Useful; -} - /// If a rewrite loop contains an explicit rule in empty context, propagate the /// explicit bit to all other rules appearing in empty context within the same /// loop. @@ -268,214 +181,6 @@ void RewriteSystem::processConflicts() { } } -/// Given a rewrite rule which appears exactly once in a loop -/// without context, return a new definition for this rewrite rule. -/// The new definition is the path obtained by deleting the -/// rewrite rule from the loop. -RewritePath RewritePath::splitCycleAtRule(unsigned ruleID) const { - // A cycle is a path from the basepoint to the basepoint. - // Somewhere in this path, an application of \p ruleID - // appears in an empty context. - - // First, we split the cycle into two paths: - // - // (1) A path from the basepoint to the rule's - // left hand side, - RewritePath basepointToLhs; - // (2) And a path from the rule's right hand side - // to the basepoint. - RewritePath rhsToBasepoint; - - // Because the rule only appears once, we know that basepointToLhs - // and rhsToBasepoint do not involve the rule itself. - - // If the rule is inverted, we have to invert the whole thing - // again at the end. - bool ruleWasInverted = false; - - bool sawRule = false; - - for (auto step : Steps) { - switch (step.Kind) { - case RewriteStep::Rule: { - if (step.getRuleID() != ruleID) - break; - - assert(!sawRule && "Rule appears more than once?"); - assert(!step.isInContext() && "Rule appears in context?"); - - ruleWasInverted = step.Inverse; - sawRule = true; - continue; - } - case RewriteStep::PrefixSubstitutions: - case RewriteStep::Shift: - case RewriteStep::Decompose: - case RewriteStep::Relation: - case RewriteStep::DecomposeConcrete: - case RewriteStep::LeftConcreteProjection: - case RewriteStep::RightConcreteProjection: - break; - } - - if (sawRule) - rhsToBasepoint.add(step); - else - basepointToLhs.add(step); - } - - // Build a path from the rule's lhs to the rule's rhs via the - // basepoint. - RewritePath result = rhsToBasepoint; - result.append(basepointToLhs); - - // We want a path from the lhs to the rhs, so invert it unless - // the rewrite step was also inverted. - if (!ruleWasInverted) - result.invert(); - - return result; -} - -/// Replace every rewrite step involving the given rewrite rule with -/// either the replacement path (or its inverse, if the step was -/// inverted). -/// -/// The replacement path is re-contextualized at each occurrence of a -/// rewrite step involving the given rule. -/// -/// Returns true if any rewrite steps were replaced; false means the -/// rule did not appear in this path. -bool RewritePath::replaceRuleWithPath(unsigned ruleID, - const RewritePath &path) { - bool foundAny = false; - - for (const auto &step : Steps) { - if (step.Kind == RewriteStep::Rule && - step.getRuleID() == ruleID) { - foundAny = true; - break; - } - } - - if (!foundAny) - return false; - - SmallVector newSteps; - - for (const auto &step : Steps) { - switch (step.Kind) { - case RewriteStep::Rule: { - // All other rewrite rules remain unchanged. - if (step.getRuleID() != ruleID) { - newSteps.push_back(step); - break; - } - - // Ok, we found a rewrite step referencing the redundant rule. - // Replace this step with the provided path. If this rewrite step has - // context, the path's own steps must be re-contextualized. - - // Keep track of rewrite step pairs which push and pop the stack. Any - // rewrite steps enclosed with a push/pop are not re-contextualized. - unsigned pushCount = 0; - - auto recontextualizeStep = [&](RewriteStep newStep) { - bool inverse = newStep.Inverse ^ step.Inverse; - - if (newStep.pushesTermsOnStack() && inverse) { - assert(pushCount > 0); - --pushCount; - } - - if (pushCount == 0) { - newStep.StartOffset += step.StartOffset; - newStep.EndOffset += step.EndOffset; - } - - newStep.Inverse = inverse; - newSteps.push_back(newStep); - - if (newStep.pushesTermsOnStack() && !inverse) { - ++pushCount; - } - }; - - // If this rewrite step is inverted, invert the entire path. - if (step.Inverse) { - for (auto newStep : llvm::reverse(path)) - recontextualizeStep(newStep); - } else { - for (auto newStep : path) - recontextualizeStep(newStep); - } - - // Rewrite steps which push and pop the stack must come in balanced pairs. - assert(pushCount == 0); - - break; - } - case RewriteStep::PrefixSubstitutions: - case RewriteStep::Shift: - case RewriteStep::Decompose: - case RewriteStep::Relation: - case RewriteStep::DecomposeConcrete: - case RewriteStep::LeftConcreteProjection: - case RewriteStep::RightConcreteProjection: - newSteps.push_back(step); - break; - } - } - - std::swap(newSteps, Steps); - return true; -} - -SmallVector -RewritePath::getRulesInEmptyContext(const MutableTerm &term, - const RewriteSystem &system) { - // Rules appearing in empty context (possibly more than once). - llvm::SmallDenseSet rulesInEmptyContext; - // The number of times each rule appears (with or without context). - llvm::SmallDenseMap ruleFrequency; - - RewritePathEvaluator evaluator(term); - for (auto step : Steps) { - switch (step.Kind) { - case RewriteStep::Rule: { - if (!step.isInContext() && !evaluator.isInContext()) - rulesInEmptyContext.insert(step.getRuleID()); - - ++ruleFrequency[step.getRuleID()]; - break; - } - - case RewriteStep::LeftConcreteProjection: - case RewriteStep::Decompose: - case RewriteStep::PrefixSubstitutions: - case RewriteStep::Shift: - case RewriteStep::Relation: - case RewriteStep::DecomposeConcrete: - case RewriteStep::RightConcreteProjection: - break; - } - - evaluator.apply(step, system); - } - - // Collect all rules that we saw exactly once in empty context. - SmallVector rulesOnceInEmptyContext; - for (auto rule : rulesInEmptyContext) { - auto found = ruleFrequency.find(rule); - assert(found != ruleFrequency.end()); - - if (found->second == 1) - rulesOnceInEmptyContext.push_back(rule); - } - - return rulesOnceInEmptyContext; -} - /// Find a rule to delete by looking through all loops for rewrite rules appearing /// once in empty context. Returns a pair consisting of a loop ID and a rule ID, /// otherwise returns None. diff --git a/lib/AST/RequirementMachine/RewriteLoop.cpp b/lib/AST/RequirementMachine/RewriteLoop.cpp index 58b766c1433d6..050161b723163 100644 --- a/lib/AST/RequirementMachine/RewriteLoop.cpp +++ b/lib/AST/RequirementMachine/RewriteLoop.cpp @@ -170,6 +170,214 @@ void RewritePath::invert() { step.invert(); } +/// Given a rewrite rule which appears exactly once in a loop +/// without context, return a new definition for this rewrite rule. +/// The new definition is the path obtained by deleting the +/// rewrite rule from the loop. +RewritePath RewritePath::splitCycleAtRule(unsigned ruleID) const { + // A cycle is a path from the basepoint to the basepoint. + // Somewhere in this path, an application of \p ruleID + // appears in an empty context. + + // First, we split the cycle into two paths: + // + // (1) A path from the basepoint to the rule's + // left hand side, + RewritePath basepointToLhs; + // (2) And a path from the rule's right hand side + // to the basepoint. + RewritePath rhsToBasepoint; + + // Because the rule only appears once, we know that basepointToLhs + // and rhsToBasepoint do not involve the rule itself. + + // If the rule is inverted, we have to invert the whole thing + // again at the end. + bool ruleWasInverted = false; + + bool sawRule = false; + + for (auto step : Steps) { + switch (step.Kind) { + case RewriteStep::Rule: { + if (step.getRuleID() != ruleID) + break; + + assert(!sawRule && "Rule appears more than once?"); + assert(!step.isInContext() && "Rule appears in context?"); + + ruleWasInverted = step.Inverse; + sawRule = true; + continue; + } + case RewriteStep::PrefixSubstitutions: + case RewriteStep::Shift: + case RewriteStep::Decompose: + case RewriteStep::Relation: + case RewriteStep::DecomposeConcrete: + case RewriteStep::LeftConcreteProjection: + case RewriteStep::RightConcreteProjection: + break; + } + + if (sawRule) + rhsToBasepoint.add(step); + else + basepointToLhs.add(step); + } + + // Build a path from the rule's lhs to the rule's rhs via the + // basepoint. + RewritePath result = rhsToBasepoint; + result.append(basepointToLhs); + + // We want a path from the lhs to the rhs, so invert it unless + // the rewrite step was also inverted. + if (!ruleWasInverted) + result.invert(); + + return result; +} + +/// Replace every rewrite step involving the given rewrite rule with +/// either the replacement path (or its inverse, if the step was +/// inverted). +/// +/// The replacement path is re-contextualized at each occurrence of a +/// rewrite step involving the given rule. +/// +/// Returns true if any rewrite steps were replaced; false means the +/// rule did not appear in this path. +bool RewritePath::replaceRuleWithPath(unsigned ruleID, + const RewritePath &path) { + bool foundAny = false; + + for (const auto &step : Steps) { + if (step.Kind == RewriteStep::Rule && + step.getRuleID() == ruleID) { + foundAny = true; + break; + } + } + + if (!foundAny) + return false; + + SmallVector newSteps; + + for (const auto &step : Steps) { + switch (step.Kind) { + case RewriteStep::Rule: { + // All other rewrite rules remain unchanged. + if (step.getRuleID() != ruleID) { + newSteps.push_back(step); + break; + } + + // Ok, we found a rewrite step referencing the redundant rule. + // Replace this step with the provided path. If this rewrite step has + // context, the path's own steps must be re-contextualized. + + // Keep track of rewrite step pairs which push and pop the stack. Any + // rewrite steps enclosed with a push/pop are not re-contextualized. + unsigned pushCount = 0; + + auto recontextualizeStep = [&](RewriteStep newStep) { + bool inverse = newStep.Inverse ^ step.Inverse; + + if (newStep.pushesTermsOnStack() && inverse) { + assert(pushCount > 0); + --pushCount; + } + + if (pushCount == 0) { + newStep.StartOffset += step.StartOffset; + newStep.EndOffset += step.EndOffset; + } + + newStep.Inverse = inverse; + newSteps.push_back(newStep); + + if (newStep.pushesTermsOnStack() && !inverse) { + ++pushCount; + } + }; + + // If this rewrite step is inverted, invert the entire path. + if (step.Inverse) { + for (auto newStep : llvm::reverse(path)) + recontextualizeStep(newStep); + } else { + for (auto newStep : path) + recontextualizeStep(newStep); + } + + // Rewrite steps which push and pop the stack must come in balanced pairs. + assert(pushCount == 0); + + break; + } + case RewriteStep::PrefixSubstitutions: + case RewriteStep::Shift: + case RewriteStep::Decompose: + case RewriteStep::Relation: + case RewriteStep::DecomposeConcrete: + case RewriteStep::LeftConcreteProjection: + case RewriteStep::RightConcreteProjection: + newSteps.push_back(step); + break; + } + } + + std::swap(newSteps, Steps); + return true; +} + +SmallVector +RewritePath::getRulesInEmptyContext(const MutableTerm &term, + const RewriteSystem &system) { + // Rules appearing in empty context (possibly more than once). + llvm::SmallDenseSet rulesInEmptyContext; + // The number of times each rule appears (with or without context). + llvm::SmallDenseMap ruleFrequency; + + RewritePathEvaluator evaluator(term); + for (auto step : Steps) { + switch (step.Kind) { + case RewriteStep::Rule: { + if (!step.isInContext() && !evaluator.isInContext()) + rulesInEmptyContext.insert(step.getRuleID()); + + ++ruleFrequency[step.getRuleID()]; + break; + } + + case RewriteStep::LeftConcreteProjection: + case RewriteStep::Decompose: + case RewriteStep::PrefixSubstitutions: + case RewriteStep::Shift: + case RewriteStep::Relation: + case RewriteStep::DecomposeConcrete: + case RewriteStep::RightConcreteProjection: + break; + } + + evaluator.apply(step, system); + } + + // Collect all rules that we saw exactly once in empty context. + SmallVector rulesOnceInEmptyContext; + for (auto rule : rulesInEmptyContext) { + auto found = ruleFrequency.find(rule); + assert(found != ruleFrequency.end()); + + if (found->second == 1) + rulesOnceInEmptyContext.push_back(rule); + } + + return rulesOnceInEmptyContext; +} + /// Dumps a series of rewrite steps applied to \p term. void RewritePath::dump(llvm::raw_ostream &out, MutableTerm term, @@ -223,6 +431,93 @@ void RewriteLoop::verify(const RewriteSystem &system) const { } } +/// Recompute various cached values if needed. +void RewriteLoop::recompute(const RewriteSystem &system) { + if (!Dirty) + return; + Dirty = 0; + + Useful = 0; + ProjectionCount = 0; + DecomposeCount = 0; + HasConcreteTypeAliasRule = 0; + + RewritePathEvaluator evaluator(Basepoint); + for (auto step : Path) { + switch (step.Kind) { + case RewriteStep::Rule: { + Useful |= (!step.isInContext() && !evaluator.isInContext()); + + const auto &rule = system.getRule(step.getRuleID()); + if (rule.isDerivedFromConcreteProtocolTypeAliasRule()) + HasConcreteTypeAliasRule = 1; + + break; + } + + case RewriteStep::LeftConcreteProjection: + ++ProjectionCount; + break; + + case RewriteStep::Decompose: + ++DecomposeCount; + break; + + case RewriteStep::PrefixSubstitutions: + case RewriteStep::Shift: + case RewriteStep::Relation: + case RewriteStep::DecomposeConcrete: + case RewriteStep::RightConcreteProjection: + break; + } + + evaluator.apply(step, system); + } + + RulesInEmptyContext = Path.getRulesInEmptyContext(Basepoint, system); +} + +/// A rewrite rule is redundant if it appears exactly once in a loop +/// without context. +ArrayRef +RewriteLoop::findRulesAppearingOnceInEmptyContext( + const RewriteSystem &system) const { + const_cast(this)->recompute(system); + return RulesInEmptyContext; +} + +/// The number of LeftConcreteProjection steps, used by the elimination order to +/// prioritize loops that are not concrete unification projections. +unsigned RewriteLoop::getProjectionCount( + const RewriteSystem &system) const { + const_cast(this)->recompute(system); + return ProjectionCount; +} + +/// The number of Decompose steps, used by the elimination order to prioritize +/// loops that are not concrete simplifications. +unsigned RewriteLoop::getDecomposeCount( + const RewriteSystem &system) const { + const_cast(this)->recompute(system); + return DecomposeCount; +} + +/// Returns true if the loop contains at least one concrete protocol typealias rule, +/// which have the form ([P].A.[concrete: C] => [P].A). +bool RewriteLoop::hasConcreteTypeAliasRule( + const RewriteSystem &system) const { + const_cast(this)->recompute(system); + return HasConcreteTypeAliasRule; +} + +/// The number of Decompose steps, used by the elimination order to prioritize +/// loops that are not concrete simplifications. +bool RewriteLoop::isUseful( + const RewriteSystem &system) const { + const_cast(this)->recompute(system); + return Useful; +} + void RewriteLoop::dump(llvm::raw_ostream &out, const RewriteSystem &system) const { out << Basepoint << ": "; From 4df7bf91c5e370364ec9208085ba400377bafb05 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 14:17:12 -0400 Subject: [PATCH 072/242] RequirementMachine: Pack RewriteStep more tightly --- lib/AST/RequirementMachine/RewriteLoop.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/AST/RequirementMachine/RewriteLoop.h b/lib/AST/RequirementMachine/RewriteLoop.h index 0f02aecb6f4e1..cb3da2598b4ce 100644 --- a/lib/AST/RequirementMachine/RewriteLoop.h +++ b/lib/AST/RequirementMachine/RewriteLoop.h @@ -219,11 +219,11 @@ struct RewriteStep { /// The size of the left whisker, which is the position within the term where /// the rule is being applied. In A.(X => Y).B, this is |A|=1. - unsigned StartOffset : 16; + unsigned StartOffset : 13; /// The size of the right whisker, which is the length of the remaining suffix /// after the rule is applied. In A.(X => Y).B, this is |B|=1. - unsigned EndOffset : 16; + unsigned EndOffset : 13; /// If Kind is Rule, the index of the rule in the rewrite system. /// From f810d3f38871655ca4de8d0a5ee18551951c4d45 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 14:17:33 -0400 Subject: [PATCH 073/242] RequirementMachine: Optimize Rewrite{Path,Loop}::computeNormalForm() --- .../NormalizeRewritePath.cpp | 107 ++++++++++++------ lib/AST/RequirementMachine/RewriteLoop.h | 2 +- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/lib/AST/RequirementMachine/NormalizeRewritePath.cpp b/lib/AST/RequirementMachine/NormalizeRewritePath.cpp index 4d369fe4620d0..df0e46d865639 100644 --- a/lib/AST/RequirementMachine/NormalizeRewritePath.cpp +++ b/lib/AST/RequirementMachine/NormalizeRewritePath.cpp @@ -1,4 +1,4 @@ -//===--- LeftCanonicalForm.cpp - Left canonical form of a rewrite path ----===// +//===--- NormalizeRewritePath.cpp - Canonical form of a rewrite path ------===// // // This source file is part of the Swift.org open source project // @@ -144,24 +144,36 @@ bool RewriteStep::maybeSwapRewriteSteps(RewriteStep &other, /// This does not change either endpoint of the path, and the path does /// not necessarily need to be a loop. bool RewritePath::computeFreelyReducedForm() { - SmallVector newSteps; - bool changed = false; - - for (const auto &step : Steps) { - if (!newSteps.empty() && - newSteps.back().isInverseOf(step)) { - changed = true; - newSteps.pop_back(); - continue; + unsigned j = 0; + + for (unsigned i = 0, e = Steps.size(); i < e; ++i) { + // Pre-condition: + // - Steps in the range [0, j-1] are freely reduced. + // - Steps in the range [i, e-1] remain to be considered. + if (j > 0) { + // If Steps[j-1] and Steps[i] are inverses of each other, + // discard both Steps[j-1] and Steps[i]. + const auto &otherStep = Steps[j - 1]; + const auto &step = Steps[i]; + + if (otherStep.isInverseOf(step)) { + --j; + continue; + } } - newSteps.push_back(step); + // Post-condition: + // - Steps in the range [0, j] are freely reduced. + // - Steps in the range [i+1, e-1] remain to be considered. + Steps[j] = Steps[i]; + ++j; } - if (!changed) + if (j == Steps.size()) return false; - std::swap(newSteps, Steps); - return changed; + + Steps.erase(Steps.begin() + j, Steps.end()); + return true; } /// Apply the interchange rule until fixed point (see maybeSwapRewriteSteps()). @@ -169,25 +181,54 @@ bool RewritePath::computeLeftCanonicalForm(const RewriteSystem &system) { bool changed = false; for (unsigned i = 1, e = Steps.size(); i < e; ++i) { - auto &prevStep = Steps[i - 1]; - auto &step = Steps[i]; + // Pre-condition: steps in the range [0, i-1] are in left-canonical + // normal form. + // + // If Steps[i] can be swapped with Steps[i-1], swap them, and check + // if Steps[i-1] (the old Steps[i]) can be swapped with Steps[i-2], + // etc. + for (unsigned j = i; j >= 1; --j) { + auto &prevStep = Steps[j - 1]; + auto &step = Steps[j]; + + if (!prevStep.maybeSwapRewriteSteps(step, system)) + break; - if (prevStep.maybeSwapRewriteSteps(step, system)) changed = true; + } + + // Post-condition: steps in the range [0, i] are in left-canonical + // normal form. } return changed; } /// Compute freely-reduced left-canonical normal form of a path. -void RewritePath::computeNormalForm(const RewriteSystem &system) { - // FIXME: This can be more efficient. - bool changed; - do { - changed = false; - changed |= computeFreelyReducedForm(); - changed |= computeLeftCanonicalForm(system); - } while (changed); +bool RewritePath::computeNormalForm(const RewriteSystem &system) { + // Note that computeFreelyReducedForm() and computeLeftCanonicalForm() + // are both idempotent, but their composition is not. + + // Begin by freely reducing the path. + bool changed = computeFreelyReducedForm(); + + // Then, bring it into left canonical form. + while (computeLeftCanonicalForm(system)) { + changed = true; + + // If it was not already in left-canonical form, freely reduce it + // again. + if (!computeFreelyReducedForm()) { + // If it was already freely reduced, then we're done, because it + // is freely reduced *and* in left-canonical form. + break; + } + + // Otherwise, perform another round, since freely reducing may have + // opened up new opportunities for left-canonicalization. + } + + return changed; } /// Given a path that is a loop around the given basepoint, cancels out @@ -227,15 +268,9 @@ bool RewritePath::computeCyclicallyReducedForm(MutableTerm &basepoint, /// Compute cyclically-reduced left-canonical normal form of a loop. void RewriteLoop::computeNormalForm(const RewriteSystem &system) { - // FIXME: This can be more efficient. - bool changed; - do { - changed = false; - changed |= Path.computeFreelyReducedForm(); - changed |= Path.computeCyclicallyReducedForm(Basepoint, system); - changed |= Path.computeLeftCanonicalForm(system); - - if (changed) - markDirty(); - } while (changed); + bool changed = Path.computeNormalForm(system); + changed |= Path.computeCyclicallyReducedForm(Basepoint, system); + + if (changed) + markDirty(); } \ No newline at end of file diff --git a/lib/AST/RequirementMachine/RewriteLoop.h b/lib/AST/RequirementMachine/RewriteLoop.h index cb3da2598b4ce..4e5ae15c12d54 100644 --- a/lib/AST/RequirementMachine/RewriteLoop.h +++ b/lib/AST/RequirementMachine/RewriteLoop.h @@ -421,7 +421,7 @@ class RewritePath { bool computeLeftCanonicalForm(const RewriteSystem &system); - void computeNormalForm(const RewriteSystem &system); + bool computeNormalForm(const RewriteSystem &system); void dump(llvm::raw_ostream &out, MutableTerm term, From 86561b8865e993fda63ae183f4c9f293b6eb98ba Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 14:23:49 -0400 Subject: [PATCH 074/242] RequirementMachine: Generalize RewritePath::replaceRuleWithPath() to replaceRulesWithPaths() --- lib/AST/RequirementMachine/RewriteLoop.cpp | 27 +++++++++++++++------- lib/AST/RequirementMachine/RewriteLoop.h | 2 ++ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/lib/AST/RequirementMachine/RewriteLoop.cpp b/lib/AST/RequirementMachine/RewriteLoop.cpp index 050161b723163..6a670252f09cf 100644 --- a/lib/AST/RequirementMachine/RewriteLoop.cpp +++ b/lib/AST/RequirementMachine/RewriteLoop.cpp @@ -248,13 +248,13 @@ RewritePath RewritePath::splitCycleAtRule(unsigned ruleID) const { /// /// Returns true if any rewrite steps were replaced; false means the /// rule did not appear in this path. -bool RewritePath::replaceRuleWithPath(unsigned ruleID, - const RewritePath &path) { +bool RewritePath::replaceRulesWithPaths( + llvm::function_ref fn) { bool foundAny = false; for (const auto &step : Steps) { if (step.Kind == RewriteStep::Rule && - step.getRuleID() == ruleID) { + fn(step.getRuleID()) != nullptr) { foundAny = true; break; } @@ -268,13 +268,13 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID, for (const auto &step : Steps) { switch (step.Kind) { case RewriteStep::Rule: { - // All other rewrite rules remain unchanged. - if (step.getRuleID() != ruleID) { + auto *replacementPath = fn(step.getRuleID()); + if (replacementPath == nullptr) { newSteps.push_back(step); break; } - // Ok, we found a rewrite step referencing the redundant rule. + // Ok, we found a rewrite step referencing a redundant rule. // Replace this step with the provided path. If this rewrite step has // context, the path's own steps must be re-contextualized. @@ -305,10 +305,10 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID, // If this rewrite step is inverted, invert the entire path. if (step.Inverse) { - for (auto newStep : llvm::reverse(path)) + for (auto newStep : llvm::reverse(*replacementPath)) recontextualizeStep(newStep); } else { - for (auto newStep : path) + for (auto newStep : *replacementPath) recontextualizeStep(newStep); } @@ -333,6 +333,17 @@ bool RewritePath::replaceRuleWithPath(unsigned ruleID, return true; } +bool RewritePath::replaceRuleWithPath(unsigned ruleID, + const RewritePath &path) { + return replaceRulesWithPaths( + [&](unsigned otherRuleID) -> const RewritePath * { + if (ruleID == otherRuleID) + return &path; + + return nullptr; + }); +} + SmallVector RewritePath::getRulesInEmptyContext(const MutableTerm &term, const RewriteSystem &system) { diff --git a/lib/AST/RequirementMachine/RewriteLoop.h b/lib/AST/RequirementMachine/RewriteLoop.h index 4e5ae15c12d54..25350569b4ac8 100644 --- a/lib/AST/RequirementMachine/RewriteLoop.h +++ b/lib/AST/RequirementMachine/RewriteLoop.h @@ -407,6 +407,8 @@ class RewritePath { RewritePath splitCycleAtRule(unsigned ruleID) const; + bool replaceRulesWithPaths(llvm::function_ref fn); + bool replaceRuleWithPath(unsigned ruleID, const RewritePath &path); SmallVector getRulesInEmptyContext(const MutableTerm &term, From afafede086dc0b56d9a654affab2b8a4938e6355 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 16:24:06 -0400 Subject: [PATCH 075/242] RequirementMachine: Support for RewriteStep::Relation in computeNormalForm() --- .../NormalizeRewritePath.cpp | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/lib/AST/RequirementMachine/NormalizeRewritePath.cpp b/lib/AST/RequirementMachine/NormalizeRewritePath.cpp index df0e46d865639..a98d5dd824ea1 100644 --- a/lib/AST/RequirementMachine/NormalizeRewritePath.cpp +++ b/lib/AST/RequirementMachine/NormalizeRewritePath.cpp @@ -57,6 +57,9 @@ bool RewriteStep::isInverseOf(const RewriteStep &other) const { case RewriteStep::Rule: return Arg == other.Arg; + case RewriteStep::Relation: + return Arg == other.Arg; + default: return false; } @@ -67,8 +70,7 @@ bool RewriteStep::isInverseOf(const RewriteStep &other) const { bool RewriteStep::maybeSwapRewriteSteps(RewriteStep &other, const RewriteSystem &system) { - if (Kind != RewriteStep::Rule || - other.Kind != RewriteStep::Rule) + if (Kind != other.Kind) return false; // Two rewrite steps are _orthogonal_ if they rewrite disjoint subterms @@ -122,22 +124,43 @@ bool RewriteStep::maybeSwapRewriteSteps(RewriteStep &other, // other.StartOffset = |A|+|V|+|B| // other.EndOffset = |C| - const auto &rule = system.getRule(Arg); - auto lhs = (Inverse ? rule.getRHS() : rule.getLHS()); - auto rhs = (Inverse ? rule.getLHS() : rule.getRHS()); + if (Kind == RewriteStep::Rule) { + const auto &rule = system.getRule(Arg); + auto lhs = (Inverse ? rule.getRHS() : rule.getLHS()); + auto rhs = (Inverse ? rule.getLHS() : rule.getRHS()); - const auto &otherRule = system.getRule(other.Arg); - auto otherLHS = (other.Inverse ? otherRule.getRHS() : otherRule.getLHS()); - auto otherRHS = (other.Inverse ? otherRule.getLHS() : otherRule.getRHS()); + const auto &otherRule = system.getRule(other.Arg); + auto otherLHS = (other.Inverse ? otherRule.getRHS() : otherRule.getLHS()); + auto otherRHS = (other.Inverse ? otherRule.getLHS() : otherRule.getRHS()); - if (StartOffset < other.StartOffset + otherLHS.size()) - return false; + if (StartOffset < other.StartOffset + otherLHS.size()) + return false; - std::swap(*this, other); - EndOffset += (lhs.size() - rhs.size()); - other.StartOffset += (otherRHS.size() - otherLHS.size()); + std::swap(*this, other); + EndOffset += (lhs.size() - rhs.size()); + other.StartOffset += (otherRHS.size() - otherLHS.size()); - return true; + return true; + } else if (Kind == RewriteStep::Relation) { + const auto &relation = system.getRelation(Arg); + auto lhs = (Inverse ? relation.second : relation.first); + auto rhs = (Inverse ? relation.first : relation.second); + + const auto &otherRelation = system.getRelation(other.Arg); + auto otherLHS = (other.Inverse ? otherRelation.second : otherRelation.first); + auto otherRHS = (other.Inverse ? otherRelation.first : otherRelation.second); + + if (StartOffset < other.StartOffset + otherLHS.size()) + return false; + + std::swap(*this, other); + EndOffset += (lhs.size() - rhs.size()); + other.StartOffset += (otherRHS.size() - otherLHS.size()); + + return true; + } + + return false; } /// Cancels out adjacent rewrite steps that are inverses of each other. From 2a634ba4684e6ffb4cafe82f1b58e9723bcc18e2 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 16:27:13 -0400 Subject: [PATCH 076/242] RequirementMachine: Optimize RewriteSystem::normalizeRedundantRules() --- .../RequirementMachine/HomotopyReduction.cpp | 50 +++++++++++++++---- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/lib/AST/RequirementMachine/HomotopyReduction.cpp b/lib/AST/RequirementMachine/HomotopyReduction.cpp index 81f6dd47db97a..baf916ccc1aa3 100644 --- a/lib/AST/RequirementMachine/HomotopyReduction.cpp +++ b/lib/AST/RequirementMachine/HomotopyReduction.cpp @@ -385,12 +385,6 @@ findRuleToDelete(EliminationPredicate isRedundantRuleFn) { /// occurrences of the rule in all loops with the replacement path. void RewriteSystem::deleteRule(unsigned ruleID, const RewritePath &replacementPath) { - // Replace all occurrences of the rule with the replacement path in - // all redundant rule paths recorded so far. - for (auto &pair : RedundantRules) { - (void) pair.second.replaceRuleWithPath(ruleID, replacementPath); - } - // Replace all occurrences of the rule with the replacement path in // all remaining rewrite loops. for (unsigned loopID : indices(Loops)) { @@ -458,8 +452,34 @@ void RewriteSystem::performHomotopyReduction( } void RewriteSystem::normalizeRedundantRules() { - for (auto &pair : RedundantRules) { + llvm::DenseMap RedundantRuleMap; + + // A redundant path in the range [0, i-1] might contain rewrite steps naming + // rules that subsequently became redundant in the range [i, e-1]. + // + // We back-substitute later rules into earlier paths here. + for (unsigned i = 0, e = RedundantRules.size(); i < e; ++i) { + // Pre-condition: Redundant paths in the range [i+1, e-1] do not involve + // any other redundant rules. + unsigned j = e - i - 1; + + // Replace all occurrences of redundant rules with their path at + // RedundantRules[i]. + auto &pair = RedundantRules[j]; + pair.second.replaceRulesWithPaths( + [&](unsigned ruleID) -> RewritePath * { + auto found = RedundantRuleMap.find(ruleID); + if (found != RedundantRuleMap.end()) + return &RedundantRules[found->second].second; + + return nullptr; + }); pair.second.computeNormalForm(*this); + + RedundantRuleMap[RedundantRules[j].first] = j; + + // Post-condition: the path for RedundantRules[i] does not contain any + // redundant rules. } if (Debug.contains(DebugFlags::RedundantRules)) { @@ -616,12 +636,12 @@ void RewriteSystem::minimizeRewriteSystem() { return false; }); + normalizeRedundantRules(); + // Check invariants after homotopy reduction. verifyRewriteLoops(); verifyRedundantConformances(redundantConformances); verifyMinimizedRules(redundantConformances); - - normalizeRedundantRules(); } /// Returns flags indicating if the rewrite system has unresolved or @@ -826,5 +846,17 @@ void RewriteSystem::verifyMinimizedRules( dump(llvm::errs()); abort(); } + + for (const auto &step : pair.second) { + if (step.Kind == RewriteStep::Rule) { + const auto &rule = getRule(step.getRuleID()); + if (rule.isRedundant()) { + llvm::errs() << "Redundant requirement path contains a redundant " + "rule " << rule << "\n"; + dump(llvm::errs()); + abort(); + } + } + } } } From 8b99b9f77be030fe6bc2b64eb0b78dfd1576cdd1 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 15 Mar 2022 10:15:01 -0700 Subject: [PATCH 077/242] [NFC] Exported printing query function. Allow other compilation units to refer to isFunctionSelectedForPrinting by declaring it extern. Maybe at some point it might be nice to put this into a header related to printing. --- lib/SILOptimizer/PassManager/PassManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILOptimizer/PassManager/PassManager.cpp b/lib/SILOptimizer/PassManager/PassManager.cpp index 637cf945081ab..8e1680d787fad 100644 --- a/lib/SILOptimizer/PassManager/PassManager.cpp +++ b/lib/SILOptimizer/PassManager/PassManager.cpp @@ -167,7 +167,7 @@ static llvm::cl::optgetName())) return false; From a023cf986ba5bb85ed8f97e2bf98349475e0917d Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 15 Mar 2022 11:52:17 -0700 Subject: [PATCH 078/242] [cxx-interop] docs, fixup to c++ interop status page after review comments --- docs/CppInteroperability/CppInteroperabilityStatus.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/CppInteroperability/CppInteroperabilityStatus.md b/docs/CppInteroperability/CppInteroperabilityStatus.md index bbd82dad64cef..d749d5df7520e 100644 --- a/docs/CppInteroperability/CppInteroperabilityStatus.md +++ b/docs/CppInteroperability/CppInteroperabilityStatus.md @@ -31,7 +31,7 @@ This status table describes which of the following C++ language features can be | Typedefs / Type aliases | Yes | | Global Variables | Yes | | Namespaces | Yes | -| Inline Namespaces | Yes, with some known issues | +| Inline Namespaces | Yes, with some known issues (https://bugs.swift.org/browse/SR-15956) | | Exceptions | No | | Fields | Yes | | Member functions | Yes. Some value category overloads aren't imported | @@ -54,8 +54,8 @@ The following C++ code patterns or language features have specific mappings to S | **C++ Language Feature** | **Imported Into Swift** | |------------------------------------------------------|------------------------------------------------------------------------------------------| -| `get`/`set` member functions | Imported as computed property (since Swift-5.7) | -| `const`/non-`const` member function overload set | Both overloads are imported as a method, with non-`const` method being renamed to `mutating…`. (since Swift-5.7). This will change in a future version of Swift | +| `get`/`set` member functions | Imported as computed property (starting from Swift-5.7) | +| `const`/non-`const` member function overload set | Both overloads are imported as a method, with non-`const` method being renamed to `mutating…` (starting from Swift-5.7). The renaming logic will change in a future version of Swift, and non-`const` methods won't be renamed | Unless stated otherwise (i.e., imported reference types) all Swift features work with imported types. For example: use in generic contexts, protocol conformance, extensions, etc. @@ -63,16 +63,15 @@ Unless stated otherwise (i.e., imported reference types) all Swift features work ### C++ Standard Library Support -Libc++ can be imported and used from Swift. +Parts of libc++ can be imported and used from Swift. -This status table describes which of the following C++ standard library features have some experimental support for using them in Swift: +This status table describes which of the following C++ standard library features have some experimental support for using them in Swift. Please note that this is not a comprehensive list and other libc++ APIs that use the above supported C++ language features could be imported into Swift. | **C++ Standard Library Feature** | **Can Be Used From Swift** | |------------------------------------|----------------------------------------------| | `std::string` | Yes | | `std::vector` | Yes | - ## Known Issues ### Inline Namespaces From 23e56e314c39d4214ac2b7a6b7fe50b8b9306398 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Sun, 13 Mar 2022 17:42:45 -0700 Subject: [PATCH 079/242] [cxx-interop] add OBJC and cplusplus conditions for emitted header includes This will allow us to generate a unified clang header. --- lib/PrintAsClang/PrintAsClang.cpp | 65 +++++++++++++++++++------------ test/PrintAsCxx/empty.swift | 15 +++++-- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/lib/PrintAsClang/PrintAsClang.cpp b/lib/PrintAsClang/PrintAsClang.cpp index ec23d0b12416e..f6910759a596e 100644 --- a/lib/PrintAsClang/PrintAsClang.cpp +++ b/lib/PrintAsClang/PrintAsClang.cpp @@ -13,7 +13,6 @@ #include "swift/PrintAsClang/PrintAsClang.h" #include "ModuleContentsWriter.h" -#include "OutputLanguageMode.h" #include "swift/AST/ASTContext.h" #include "swift/AST/Module.h" @@ -28,7 +27,28 @@ using namespace swift; static void writePrologue(raw_ostream &out, ASTContext &ctx, - StringRef macroGuard, OutputLanguageMode Lang) { + StringRef macroGuard) { + auto emitCxxConditional = [&](llvm::function_ref cxxCase, + llvm::function_ref cCase = {}) { + out << "#if defined(__cplusplus)\n"; + cxxCase(); + if (cCase) { + out << "#else\n"; + cCase(); + } + out << "#endif\n"; + }; + auto emitObjCConditional = [&](llvm::function_ref objcCase, + llvm::function_ref nonObjCCase = {}) { + out << "#if defined(__OBJC__)\n"; + objcCase(); + if (nonObjCCase) { + out << "#else\n"; + nonObjCCase(); + } + out << "#endif\n"; + }; + out << "// Generated by " << version::getSwiftFullVersion(ctx.LangOpts.EffectiveLanguageVersion) << "\n" @@ -57,16 +77,18 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, "#endif\n" "\n" "#pragma clang diagnostic ignored \"-Wauto-import\"\n"; - if (Lang == OutputLanguageMode::Cxx) { - out << "#include \n" - "#include \n" - "#include \n"; - } else { - out << "#include \n" - "#include \n" - "#include \n" - "#include \n"; - } + emitObjCConditional([&] { out << "#include \n"; }); + emitCxxConditional( + [&] { + out << "#include \n" + "#include \n" + "#include \n"; + }, + [&] { + out << "#include \n" + "#include \n" + "#include \n"; + }); out << "\n" "#if !defined(SWIFT_TYPEDEFS)\n" "# define SWIFT_TYPEDEFS 1\n" @@ -254,11 +276,11 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, "#else\n" "# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)\n" "#endif\n"; - if (Lang == OutputLanguageMode::ObjC) { + emitObjCConditional([&] { out << "#if !defined(IBSegueAction)\n" "# define IBSegueAction\n" "#endif\n"; - } + }); out << "#if !defined(SWIFT_EXTERN)\n" "# if defined(__cplusplus)\n" "# define SWIFT_EXTERN extern \"C\"\n" @@ -266,18 +288,15 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, "# define SWIFT_EXTERN extern\n" "# endif\n" "#endif\n"; - auto emitMacro = [&](StringRef name, StringRef value) { + auto emitMacro = [&](StringRef name, StringRef value = "") { out << "#if !defined(" << name << ")\n"; out << "# define " << name << " " << value << "\n"; out << "#endif\n"; }; emitMacro("SWIFT_CALL", "__attribute__((swiftcall))"); // SWIFT_NOEXCEPT applies 'noexcept' in C++ mode only. - out << "#if defined(__cplusplus)\n"; - emitMacro("SWIFT_NOEXCEPT", "noexcept"); - out << "#else\n"; - emitMacro("SWIFT_NOEXCEPT", ""); - out << "#endif\n"; + emitCxxConditional([&] { emitMacro("SWIFT_NOEXCEPT", "noexcept"); }, + [&] { emitMacro("SWIFT_NOEXCEPT"); }); static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, "need to add SIMD typedefs here if max elements is increased"); } @@ -431,8 +450,7 @@ bool swift::printAsObjC(raw_ostream &os, ModuleDecl *M, std::string moduleContentsBuf; llvm::raw_string_ostream moduleContents{moduleContentsBuf}; printModuleContentsAsObjC(moduleContents, imports, *M); - writePrologue(os, M->getASTContext(), computeMacroGuard(M), - OutputLanguageMode::ObjC); + writePrologue(os, M->getASTContext(), computeMacroGuard(M)); writeImports(os, imports, *M, bridgingHeader); writePostImportPrologue(os, *M); os << moduleContents.str(); @@ -448,8 +466,7 @@ bool swift::printAsCXX(raw_ostream &os, ModuleDecl *M) { std::string moduleContentsBuf; llvm::raw_string_ostream moduleContents{moduleContentsBuf}; printModuleContentsAsCxx(moduleContents, imports, *M); - writePrologue(os, M->getASTContext(), computeMacroGuard(M), - OutputLanguageMode::Cxx); + writePrologue(os, M->getASTContext(), computeMacroGuard(M)); writePostImportPrologue(os, *M); os << moduleContents.str(); writeEpilogue(os); diff --git a/test/PrintAsCxx/empty.swift b/test/PrintAsCxx/empty.swift index 347480c669392..0b3c6c64c9b07 100644 --- a/test/PrintAsCxx/empty.swift +++ b/test/PrintAsCxx/empty.swift @@ -27,9 +27,18 @@ // CHECK-NEXT: # define __has_warning(x) 0 // CHECK-NEXT: #endif -// CHECK-LABEL: #include -// CHECK: #include -// CHECK: #include +// CHECK-LABEL: #if defined(__OBJC__) +// CHECK-NEXT: #include +// CHECK-NEXT: #endif +// CHECK-NEXT: #if defined(__cplusplus) +// CHECK-NEXT: #include +// CHECK-NEXT: #include +// CHECK-NEXT: #include +// CHECK-NEXT: #else +// CHECK-NEXT: #include +// CHECK-NEXT: #include +// CHECK-NEXT: #include +// CHECK-NEXT: #endif // CHECK-LABEL: !defined(SWIFT_TYPEDEFS) // CHECK-NEXT: # define SWIFT_TYPEDEFS 1 From bb9014017a06b73e569710e18ca7fbd8ae439786 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Wed, 9 Mar 2022 14:26:34 -0800 Subject: [PATCH 080/242] Offer LocalTestingDistributedActorSystem Add local-only actor system `LocalTestingDistributedActorSystem` to make code and tutorial development easier. rdar://89580224 --- stdlib/public/Distributed/CMakeLists.txt | 3 +- .../Distributed/DistributedActorSystem.swift | 4 +- .../LocalTestingDistributedActorSystem.swift | 159 ++++++++++++++++++ 3 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift diff --git a/stdlib/public/Distributed/CMakeLists.txt b/stdlib/public/Distributed/CMakeLists.txt index 96bb0ab3fd478..995882eb158ed 100644 --- a/stdlib/public/Distributed/CMakeLists.txt +++ b/stdlib/public/Distributed/CMakeLists.txt @@ -2,7 +2,7 @@ # # This source file is part of the Swift.org open source project # -# Copyright (c) 2019 - 2020 Apple Inc. and the Swift project authors +# Copyright (c) 2019 - 2022 Apple Inc. and the Swift project authors # Licensed under Apache License v2.0 with Runtime Library Exception # # See https://swift.org/LICENSE.txt for license information @@ -19,6 +19,7 @@ add_swift_target_library(swift_Distributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I DistributedActor.swift DistributedActorSystem.swift DistributedMetadata.swift + LocalTestingDistributedActorSystem.swift SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_FREEBSD Glibc diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index 841c132b9d234..9519f281b4777 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -78,7 +78,7 @@ public protocol DistributedActorSystem: Sendable { /// /// The `actor.id` of the passed actor must be an `ActorID` that this system previously has assigned. /// - /// If the `actorReady` gets called with some unknown ID, it should crash immediately as it signifies some + /// If `actorReady` gets called with some unknown ID, it should crash immediately as it signifies some /// very unexpected use of the system. /// /// - Parameter actor: reference to the (local) actor that was just fully initialized. @@ -93,7 +93,7 @@ public protocol DistributedActorSystem: Sendable { /// and not re-cycled by the system), i.e. if it is called during a failure to initialize completely, /// the call from the actor's deinitalizer will not happen (as under these circumstances, `deinit` will be run). /// - /// If the `actorReady` gets called with some unknown ID, it should crash immediately as it signifies some + /// If `resignID` gets called with some unknown ID, it should crash immediately as it signifies some /// very unexpected use of the system. /// /// - Parameter id: the id of an actor managed by this system that has begun its `deinit`. diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift new file mode 100644 index 0000000000000..bd6a9352211b0 --- /dev/null +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -0,0 +1,159 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// TODO(distributed): not thread safe... +public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable { + public typealias ActorID = ActorAddress + public typealias InvocationEncoder = LocalInvocationEncoder + public typealias InvocationDecoder = LocalInvocationDecoder + public typealias SerializationRequirement = Codable + + private var activeActors: [ActorID: any DistributedActor] = [:] + + private var idProvider: ActorIDProvider = ActorIDProvider() + private var assignedIDs: Set = [] + + public init() {} + + public func resolve(id: ActorID, as actorType: Act.Type) + throws -> Act? where Act: DistributedActor { + guard let anyActor = self.activeActors[id] else { + throw LocalTestingDistributedActorSystemError(message: "Unable to locate id '\(id)' locally") + } + guard let actor = anyActor as? Act else { + throw LocalTestingDistributedActorSystemError(message: "Failed to resolve id '\(id)' as \(Act.Type)") + } + return actor + } + + public func assignID(_ actorType: Act.Type) -> ActorID + where Act: DistributedActor { + let id = ActorAddress(parse: "\(self.idProvider.next())") + print("| assign id: \(id) for \(actorType)") + self.assignedIDs.insert(id) + return id + } + + public func actorReady(_ actor: Act) + where Act: DistributedActor, + Act.ID == ActorID { + guard self.assignedIDs.contains(actor.id) else { + fatalError("Attempted to mark an unknown actor '\(actor.id)' ready") + } + print("| actor ready: \(actor)") + self.activeActors[actor.id] = actor + } + + public func resignID(_ id: ActorID) { + guard self.assignedIDs.contains(actor.id) else { + fatalError("Attempted to resign unknown id '\(id)'") + } + self.activeActors.removeValue(forKey: id) + } + + public func makeInvocationEncoder() -> InvocationEncoder { + .init() + } + + public func remoteCall( + on actor: Act, + target: RemoteCallTarget, + invocation: inout InvocationEncoder, + throwing errorType: Err.Type, + returning returnType: Res.Type + ) async throws -> Res + where Act: DistributedActor, + Act.ID == ActorID, + Err: Error, + Res: SerializationRequirement { + fatalError("Attempted to make remote call on actor \(actor) in a local-only actor system") + } + + public func remoteCallVoid( + on actor: Act, + target: RemoteCallTarget, + invocation: inout InvocationEncoder, + throwing errorType: Err.Type + ) async throws + where Act: DistributedActor, + Act.ID == ActorID, + Err: Error { + fatalError("Attempted to make remote call on actor \(actor) in a local-only actor system") + } + + // TODO(distributed): not thread safe... + private struct ActorIDProvider { + private var counter: Int = 0 + + init() {} + + mutating func next() -> ActorAddress { + self.counter += 1 + return ActorAddress(parse: "\(self.counter)") + } + } +} + +public struct LocalInvocationEncoder: DistributedTargetInvocationEncoder { + public typealias SerializationRequirement = Codable + + public mutating func recordGenericSubstitution(_ type: T.Type) throws { + fatalError("Attempted to call encoder method in a local-only actor system") + } + + public mutating func recordArgument(_ argument: Argument) throws { + fatalError("Attempted to call encoder method in a local-only actor system") + } + + public mutating func recordErrorType(_ type: E.Type) throws { + fatalError("Attempted to call encoder method in a local-only actor system") + } + + public mutating func recordReturnType(_ type: R.Type) throws { + fatalError("Attempted to call encoder method in a local-only actor system") + } + + public mutating func doneRecording() throws { + fatalError("Attempted to call encoder method in a local-only actor system") + } +} + +public class LocalInvocationDecoder : DistributedTargetInvocationDecoder { + public typealias SerializationRequirement = Codable + + public func decodeGenericSubstitutions() throws -> [Any.Type] { + fatalError("Attempted to call decoder method in a local-only actor system") + } + + public func decodeNextArgument() throws -> Argument { + fatalError("Attempted to call decoder method in a local-only actor system") + } + + public func decodeErrorType() throws -> Any.Type? { + fatalError("Attempted to call decoder method in a local-only actor system") + } + + public func decodeReturnType() throws -> Any.Type? { + fatalError("Attempted to call decoder method in a local-only actor system") + } +} + +// === errors ---------------------------------------------------------------- + +@available(SwiftStdlib 5.7, *) +public struct LocalTestingDistributedActorSystemError: DistributedActorSystemError { + public let message: String + + public init(message: String) { + self.message = message + } +} From 97da20585e409a82829851d0e507a10a06f71190 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Wed, 9 Mar 2022 16:55:52 -0800 Subject: [PATCH 081/242] Fix build errors --- .../LocalTestingDistributedActorSystem.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index bd6a9352211b0..b086fa790db30 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -10,11 +10,13 @@ // //===----------------------------------------------------------------------===// +import Swift + // TODO(distributed): not thread safe... public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable { public typealias ActorID = ActorAddress - public typealias InvocationEncoder = LocalInvocationEncoder - public typealias InvocationDecoder = LocalInvocationDecoder + public typealias InvocationEncoder = LocalTestingInvocationEncoder + public typealias InvocationDecoder = LocalTestingInvocationDecoder public typealias SerializationRequirement = Codable private var activeActors: [ActorID: any DistributedActor] = [:] @@ -103,7 +105,7 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ } } -public struct LocalInvocationEncoder: DistributedTargetInvocationEncoder { +public struct LocalTestingInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws { @@ -127,7 +129,7 @@ public struct LocalInvocationEncoder: DistributedTargetInvocationEncoder { } } -public class LocalInvocationDecoder : DistributedTargetInvocationDecoder { +public class LocalTestingInvocationDecoder : DistributedTargetInvocationDecoder { public typealias SerializationRequirement = Codable public func decodeGenericSubstitutions() throws -> [Any.Type] { From 290506d006110f9dbd2a7ce334b53ca393241450 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Wed, 9 Mar 2022 17:01:09 -0800 Subject: [PATCH 082/242] Add LocalTestingActorAddress --- .../LocalTestingDistributedActorSystem.swift | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index b086fa790db30..34f572efc7bd2 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -12,9 +12,27 @@ import Swift +public struct LocalTestingActorAddress: Hashable, Sendable, Codable { + public let address: String + + public init(parse address: String) { + self.address = address + } + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + self.address = try container.decode(String.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.address) + } +} + // TODO(distributed): not thread safe... public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable { - public typealias ActorID = ActorAddress + public typealias ActorID = LocalTestingActorAddress public typealias InvocationEncoder = LocalTestingInvocationEncoder public typealias InvocationDecoder = LocalTestingInvocationDecoder public typealias SerializationRequirement = Codable @@ -98,9 +116,9 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ init() {} - mutating func next() -> ActorAddress { + mutating func next() -> LocalTestingActorAddress { self.counter += 1 - return ActorAddress(parse: "\(self.counter)") + return LocalTestingActorAddress(parse: "\(self.counter)") } } } From 1dd0604cf55c6957982c199056504cc994fc0bd0 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Wed, 9 Mar 2022 17:02:43 -0800 Subject: [PATCH 083/242] Don't print --- .../public/Distributed/LocalTestingDistributedActorSystem.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index 34f572efc7bd2..aa2ccc2c26d32 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -58,7 +58,6 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ public func assignID(_ actorType: Act.Type) -> ActorID where Act: DistributedActor { let id = ActorAddress(parse: "\(self.idProvider.next())") - print("| assign id: \(id) for \(actorType)") self.assignedIDs.insert(id) return id } @@ -69,7 +68,6 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ guard self.assignedIDs.contains(actor.id) else { fatalError("Attempted to mark an unknown actor '\(actor.id)' ready") } - print("| actor ready: \(actor)") self.activeActors[actor.id] = actor } From 2e5d4b64e64c82b72390a7d2525935c53a7c25f4 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Wed, 9 Mar 2022 18:28:06 -0800 Subject: [PATCH 084/242] Fix build errors --- .../Distributed/LocalTestingDistributedActorSystem.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index aa2ccc2c26d32..edc24d72a55d6 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -50,14 +50,14 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ throw LocalTestingDistributedActorSystemError(message: "Unable to locate id '\(id)' locally") } guard let actor = anyActor as? Act else { - throw LocalTestingDistributedActorSystemError(message: "Failed to resolve id '\(id)' as \(Act.Type)") + throw LocalTestingDistributedActorSystemError(message: "Failed to resolve id '\(id)' as \(Act.Type.self)") } return actor } public func assignID(_ actorType: Act.Type) -> ActorID where Act: DistributedActor { - let id = ActorAddress(parse: "\(self.idProvider.next())") + let id = self.idProvider.next() self.assignedIDs.insert(id) return id } @@ -72,7 +72,7 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ } public func resignID(_ id: ActorID) { - guard self.assignedIDs.contains(actor.id) else { + guard self.assignedIDs.contains(id) else { fatalError("Attempted to resign unknown id '\(id)'") } self.activeActors.removeValue(forKey: id) From c5254bb4274b5061a4c17e1bfc2ec2623ae903b9 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Wed, 9 Mar 2022 19:07:54 -0800 Subject: [PATCH 085/242] Add @available --- .../public/Distributed/LocalTestingDistributedActorSystem.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index edc24d72a55d6..d1b3de78ff6c6 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -31,6 +31,7 @@ public struct LocalTestingActorAddress: Hashable, Sendable, Codable { } // TODO(distributed): not thread safe... +@available(SwiftStdlib 5.7, *) public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable { public typealias ActorID = LocalTestingActorAddress public typealias InvocationEncoder = LocalTestingInvocationEncoder From a5adf2640b85209d57771ac2c60167f9531f5deb Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Wed, 9 Mar 2022 21:20:42 -0800 Subject: [PATCH 086/242] Workaround swift-syntax-test bug --- .../public/Distributed/LocalTestingDistributedActorSystem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index d1b3de78ff6c6..ff984b859dc31 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -38,7 +38,7 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ public typealias InvocationDecoder = LocalTestingInvocationDecoder public typealias SerializationRequirement = Codable - private var activeActors: [ActorID: any DistributedActor] = [:] + private var activeActors: [ActorID: DistributedActor] = [:] private var idProvider: ActorIDProvider = ActorIDProvider() private var assignedIDs: Set = [] From 2adcd724cb16686af4f9a7d4d8f51dbf78b79e91 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Thu, 10 Mar 2022 12:29:25 -0800 Subject: [PATCH 087/242] Make thread safe --- stdlib/public/Distributed/CMakeLists.txt | 2 +- .../LocalTestingDistributedActorSystem.swift | 108 ++++++++++++++++-- 2 files changed, 99 insertions(+), 11 deletions(-) diff --git a/stdlib/public/Distributed/CMakeLists.txt b/stdlib/public/Distributed/CMakeLists.txt index 995882eb158ed..f4658bf2c4e1e 100644 --- a/stdlib/public/Distributed/CMakeLists.txt +++ b/stdlib/public/Distributed/CMakeLists.txt @@ -26,7 +26,7 @@ add_swift_target_library(swift_Distributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I SWIFT_MODULE_DEPENDS_OPENBSD Glibc SWIFT_MODULE_DEPENDS_CYGWIN Glibc SWIFT_MODULE_DEPENDS_HAIKU Glibc - SWIFT_MODULE_DEPENDS_WINDOWS CRT + SWIFT_MODULE_DEPENDS_WINDOWS CRT WinSDK LINK_LIBRARIES ${swift_distributed_link_libraries} diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index ff984b859dc31..00370994ad833 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -12,6 +12,12 @@ import Swift +#if canImport(Glibc) +import Glibc +#elseif os(Windows) +import WinSDK +#endif + public struct LocalTestingActorAddress: Hashable, Sendable, Codable { public let address: String @@ -30,7 +36,6 @@ public struct LocalTestingActorAddress: Hashable, Sendable, Codable { } } -// TODO(distributed): not thread safe... @available(SwiftStdlib 5.7, *) public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable { public typealias ActorID = LocalTestingActorAddress @@ -39,15 +44,17 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ public typealias SerializationRequirement = Codable private var activeActors: [ActorID: DistributedActor] = [:] + private let activeActorsLock = _Lock() private var idProvider: ActorIDProvider = ActorIDProvider() private var assignedIDs: Set = [] + private let assignedIDsLock = _Lock() public init() {} public func resolve(id: ActorID, as actorType: Act.Type) throws -> Act? where Act: DistributedActor { - guard let anyActor = self.activeActors[id] else { + guard let anyActor = self.activeActorsLock.withLock({ self.activeActors[id] }) else { throw LocalTestingDistributedActorSystemError(message: "Unable to locate id '\(id)' locally") } guard let actor = anyActor as? Act else { @@ -59,24 +66,30 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ public func assignID(_ actorType: Act.Type) -> ActorID where Act: DistributedActor { let id = self.idProvider.next() - self.assignedIDs.insert(id) + self.assignedIDsLock.withLock { + self.assignedIDs.insert(id) + } return id } public func actorReady(_ actor: Act) where Act: DistributedActor, Act.ID == ActorID { - guard self.assignedIDs.contains(actor.id) else { + guard self.assignedIDsLock.withLock({ self.assignedIDs.contains(actor.id) }) else { fatalError("Attempted to mark an unknown actor '\(actor.id)' ready") } - self.activeActors[actor.id] = actor + self.activeActorsLock.withLock { + self.activeActors[actor.id] = actor + } } public func resignID(_ id: ActorID) { - guard self.assignedIDs.contains(id) else { + guard self.assignedIDsLock.withLock({ self.assignedIDs.contains(id) }) else { fatalError("Attempted to resign unknown id '\(id)'") } - self.activeActors.removeValue(forKey: id) + self.activeActorsLock.withLock { + self.activeActors.removeValue(forKey: id) + } } public func makeInvocationEncoder() -> InvocationEncoder { @@ -109,15 +122,18 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ fatalError("Attempted to make remote call on actor \(actor) in a local-only actor system") } - // TODO(distributed): not thread safe... private struct ActorIDProvider { private var counter: Int = 0 + private let counterLock = _Lock() init() {} mutating func next() -> LocalTestingActorAddress { - self.counter += 1 - return LocalTestingActorAddress(parse: "\(self.counter)") + let id: Int = self.counterLock.withLock { + self.counter += 1 + return self.counter + } + return LocalTestingActorAddress(parse: "\(id)") } } } @@ -176,3 +192,75 @@ public struct LocalTestingDistributedActorSystemError: DistributedActorSystemErr self.message = message } } + +// === lock ---------------------------------------------------------------- + +fileprivate class _Lock { + #if os(Windows) + private let underlying: UnsafeMutablePointer + #elseif os(Cygwin) || os(FreeBSD) || os(OpenBSD) + private let underlying: UnsafeMutablePointer + #elseif os(WASI) + // pthread is currently not available on WASI + #else + private let underlying: UnsafeMutablePointer + #endif + + deinit { + #if os(Windows) + // Mutexes do not need to be explicitly destroyed + #elseif os(WASI) + // WASI environment has only a single thread + #else + guard pthread_mutex_destroy(self.underlying) == 0 else { + fatalError("pthread_mutex_destroy failed") + } + #endif + + #if !os(WASI) + self.underlying.deinitialize(count: 1) + self.underlying.deallocate() + #endif + } + + init() { + #if os(Windows) + self.underlying = UnsafeMutablePointer.allocate(capacity: 1) + InitializeSRWLock(self.underlying) + #elseif os(WASI) + // WASI environment has only a single thread + #else + self.underlying = UnsafeMutablePointer.allocate(capacity: 1) + guard pthread_mutex_init(self.underlying, nil) == 0 else { + fatalError("pthread_mutex_init failed") + } + #endif + } + + @discardableResult + func withLock(_ body: () -> T) -> T { + #if os(Windows) + AcquireSRWLockExclusive(self.underlying) + #elseif os(WASI) + // WASI environment has only a single thread + #else + guard pthread_mutex_lock(self.underlying) == 0 else { + fatalError("pthread_mutex_lock failed") + } + #endif + + defer { + #if os(Windows) + ReleaseSRWLockExclusive(self.underlying) + #elseif os(WASI) + // WASI environment has only a single thread + #else + guard pthread_mutex_unlock(self.underlying) == 0 else { + fatalError("pthread_mutex_unlock failed") + } + #endif + } + + return body() + } +} From 143914a4e729e78b46db3fb463ea75004ee4ebe4 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Thu, 10 Mar 2022 16:18:36 -0800 Subject: [PATCH 088/242] Use os_unfair_lock on Apple platforms --- stdlib/public/Distributed/CMakeLists.txt | 4 +++ .../LocalTestingDistributedActorSystem.swift | 30 +++++++++++++------ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/stdlib/public/Distributed/CMakeLists.txt b/stdlib/public/Distributed/CMakeLists.txt index f4658bf2c4e1e..91801a605e7bb 100644 --- a/stdlib/public/Distributed/CMakeLists.txt +++ b/stdlib/public/Distributed/CMakeLists.txt @@ -21,6 +21,10 @@ add_swift_target_library(swift_Distributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I DistributedMetadata.swift LocalTestingDistributedActorSystem.swift + SWIFT_MODULE_DEPENDS_IOS Darwin + SWIFT_MODULE_DEPENDS_OSX Darwin + SWIFT_MODULE_DEPENDS_TVOS Darwin + SWIFT_MODULE_DEPENDS_WATCHOS Darwin SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_FREEBSD Glibc SWIFT_MODULE_DEPENDS_OPENBSD Glibc diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index 00370994ad833..654ce38ffa171 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -12,7 +12,9 @@ import Swift -#if canImport(Glibc) +#if canImport(Darwin) +import Darwin +#elseif canImport(Glibc) import Glibc #elseif os(Windows) import WinSDK @@ -196,19 +198,23 @@ public struct LocalTestingDistributedActorSystemError: DistributedActorSystemErr // === lock ---------------------------------------------------------------- fileprivate class _Lock { - #if os(Windows) + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) + private let underlying: UnsafeMutablePointer + #elseif os(Windows) private let underlying: UnsafeMutablePointer - #elseif os(Cygwin) || os(FreeBSD) || os(OpenBSD) - private let underlying: UnsafeMutablePointer #elseif os(WASI) // pthread is currently not available on WASI + #elseif os(Cygwin) || os(FreeBSD) || os(OpenBSD) + private let underlying: UnsafeMutablePointer #else private let underlying: UnsafeMutablePointer #endif deinit { - #if os(Windows) - // Mutexes do not need to be explicitly destroyed + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) + // `os_unfair_lock`s do not need to be explicitly destroyed + #elseif os(Windows) + // `SRWLOCK`s do not need to be explicitly destroyed #elseif os(WASI) // WASI environment has only a single thread #else @@ -224,7 +230,9 @@ fileprivate class _Lock { } init() { - #if os(Windows) + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) + self.underlying = UnsafeMutablePointer.allocate(capacity: 1) + #elseif os(Windows) self.underlying = UnsafeMutablePointer.allocate(capacity: 1) InitializeSRWLock(self.underlying) #elseif os(WASI) @@ -239,7 +247,9 @@ fileprivate class _Lock { @discardableResult func withLock(_ body: () -> T) -> T { - #if os(Windows) + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) + os_unfair_lock_lock(self.underlying) + #elseif os(Windows) AcquireSRWLockExclusive(self.underlying) #elseif os(WASI) // WASI environment has only a single thread @@ -250,7 +260,9 @@ fileprivate class _Lock { #endif defer { - #if os(Windows) + #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) + os_unfair_lock_unlock(self.underlying) + #elseif os(Windows) ReleaseSRWLockExclusive(self.underlying) #elseif os(WASI) // WASI environment has only a single thread From b001d5bc59f1e5c7cd9d095e0758fd02d9172b24 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Tue, 15 Mar 2022 12:33:52 -0700 Subject: [PATCH 089/242] swift-syntax-test bug fixed; remove workaround --- .../public/Distributed/LocalTestingDistributedActorSystem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index 654ce38ffa171..37b22c0b141b5 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -45,7 +45,7 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ public typealias InvocationDecoder = LocalTestingInvocationDecoder public typealias SerializationRequirement = Codable - private var activeActors: [ActorID: DistributedActor] = [:] + private var activeActors: [ActorID: any DistributedActor] = [:] private let activeActorsLock = _Lock() private var idProvider: ActorIDProvider = ActorIDProvider() From 0d2b2f2a555c78c7be4819275a1cebf174182cff Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 14 Mar 2022 18:57:50 -0700 Subject: [PATCH 090/242] Stabilize VarDecl::isLet for ParamDecls For ParamDecl instances, the value of this property is not just a function of the introducer (let/var which is a poorly-defined concept for parameters), it's a function of the specifier (inout/__owned/__shared etc). However, computing the specifier also has the side effect of flipping the introducer bits. This appears to be because while the AST uses `isLet` in a syntactic sense "did the user write 'let'?", SIL uses it in a semantic sense "is this property semantically immutable?". These two queries need to be split from one another and the callers migrated. But that is a much larger task for a later time. For now, provide the value of `ParamDecl::isImmutable` to callers since that's the more conservative of the two behaviors. The bug here is that it's possible for `getSpecifier` to *not* be called before `isLet` is called (usually in SIL). This manifested as a test output divergence on the non-asserts bots since the ASTVerifier was always calling getSpecifier, and most engineers do not build without asserts on at their desk. rdar://89237318 --- include/swift/AST/Decl.h | 5 ++--- lib/AST/Decl.cpp | 13 +++++++++++++ test/DebugInfo/debug_value_addr.swift | 2 +- .../move_function_kills_copyable_values.swift | 6 +++--- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 226b58d555c08..6910f2b27c710 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -5248,9 +5248,8 @@ class VarDecl : public AbstractStorageDecl { /// Is this an immutable 'let' property? /// - /// If this is a ParamDecl, isLet() is true iff - /// getSpecifier() == Specifier::Default. - bool isLet() const { return getIntroducer() == Introducer::Let; } + /// For \c ParamDecl instances, using \c isImmutable is preferred. + bool isLet() const; /// Is this an "async let" property? bool isAsyncLet() const; diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 59bbe67bc8934..576aaff8986d2 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6373,6 +6373,19 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const { return true; } +bool VarDecl::isLet() const { + // An awful hack that stabilizes the value of 'isLet' for ParamDecl instances. + // + // All of the callers in SIL are actually looking for the semantic + // "is immutable" predicate (present on ParamDecl) and should be migrated to + // a high-level request. Once this is done, all callers of the introducer and + // specifier setters can be removed. + if (auto *PD = dyn_cast(this)) { + return PD->isImmutable(); + } + return getIntroducer() == Introducer::Let; +} + bool VarDecl::isAsyncLet() const { return getAttrs().hasAttribute(); } diff --git a/test/DebugInfo/debug_value_addr.swift b/test/DebugInfo/debug_value_addr.swift index 06f5b187a1daf..707d42c7bbae7 100644 --- a/test/DebugInfo/debug_value_addr.swift +++ b/test/DebugInfo/debug_value_addr.swift @@ -28,7 +28,7 @@ func use(_ t : T) {} // CHECK-SIL: sil hidden @$s16debug_value_addr11GenericSelfV1xACyxGx_tcfC : $@convention(method) (@in T, @thin GenericSelf.Type) -> GenericSelf { // CHECK-SIL: bb0(%0 : $*T, %1 : $@thin GenericSelf.Type): // -// CHECK-SIL-NEXT: alloc_stack [lexical] $GenericSelf, {{var|let}}, name "self", implicit, loc {{.*}} +// CHECK-SIL-NEXT: alloc_stack [lexical] $GenericSelf, var, name "self", implicit, loc {{.*}} // CHECK-SIL-NEXT: debug_value %0 : $*T, let, name "x", argno 1, expr op_deref, loc {{.*}} struct GenericSelf { init(x: T) { diff --git a/test/SILOptimizer/move_function_kills_copyable_values.swift b/test/SILOptimizer/move_function_kills_copyable_values.swift index 0a99d9dd063ec..23439ed55c6be 100644 --- a/test/SILOptimizer/move_function_kills_copyable_values.swift +++ b/test/SILOptimizer/move_function_kills_copyable_values.swift @@ -227,10 +227,10 @@ public struct Pair { // have invalidated a part of pair. We can be less restrictive in the future. // // TODO: Why are we emitting two uses here. -public func performMoveOnOneEltOfPair(_ p: __owned Pair) { +public func performMoveOnOneEltOfPair(_ p: __owned Pair) { // expected-error {{'p' used after being moved}} let _ = p.z - let _ = _move(p.x) // expected-error {{_move applied to value that the compiler does not support checking}} - nonConsumingUse(p.y) + let _ = _move(p.x) // expected-note {{move here}} + nonConsumingUse(p.y) // expected-note 2 {{use here}} } public class KlassPair { From b47d43638e90b73b6ab3614aefff04a77ab74d1c Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 8 Mar 2022 23:49:31 -0800 Subject: [PATCH 091/242] CMake: Add `-target-min-inlining-version min` to the Swift standard library build's arguments. This will prevent issues with incorrect or missing availability checking in inlinable function bodies. Resolves rdar://89991736 --- stdlib/cmake/modules/AddSwiftStdlib.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index 2cca338cb5d53..8aad1890381d2 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -851,6 +851,9 @@ function(add_swift_target_library_single target name) list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS "-Xfrontend" "-define-availability" "-Xfrontend" "${def}") endforeach() + # Enable -target-min-inlining-version + list(APPEND SWIFTLIB_SINGLE_SWIFT_COMPILE_FLAGS "-Xfrontend" "-target-min-inlining-version" "-Xfrontend" "min") + # Don't install the Swift module content for back-deployment libraries. if (SWIFTLIB_SINGLE_BACK_DEPLOYMENT_LIBRARY) set(install_in_component "never_install") From 5b5ed561c5e26f4f1c9fa2f92e3aa223d703432f Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 15 Mar 2022 10:48:03 -0700 Subject: [PATCH 092/242] Added printing during inlining. Enable caller and callee to be printed as inlining runs. The printing is filtered based on -sil-print-function/-sil-print-functions and includes finer-grained info than those do already. The caller before and after each callee is inlined can be printed as well as the callee on its own as it exists when inlining occurs. --- .../Transforms/PerformanceInliner.cpp | 77 +++++++++++++++++-- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp index ef6456bba5c96..642b891b32d4c 100644 --- a/lib/SILOptimizer/Transforms/PerformanceInliner.cpp +++ b/lib/SILOptimizer/Transforms/PerformanceInliner.cpp @@ -49,6 +49,59 @@ llvm::cl::opt EnableVerifyAfterInlining( llvm::cl::desc("Run sil verification after inlining all found callee apply " "sites into a caller.")); +llvm::cl::opt SILPrintInliningCallee( + "sil-print-inlining-callee", llvm::cl::init(false), + llvm::cl::desc("Print functions that are inlined into other functions.")); + +llvm::cl::opt SILPrintInliningCallerBefore( + "sil-print-inlining-caller-before", llvm::cl::init(false), + llvm::cl::desc( + "Print functions into which another function is about to be inlined.")); + +llvm::cl::opt SILPrintInliningCallerAfter( + "sil-print-inlining-caller-after", llvm::cl::init(false), + llvm::cl::desc( + "Print functions into which another function has been inlined.")); + +//===----------------------------------------------------------------------===// +// Printing Helpers +//===----------------------------------------------------------------------===// + +extern bool isFunctionSelectedForPrinting(SILFunction *F); + +static void printInliningDetails(StringRef passName, SILFunction *caller, + SILFunction *callee, bool isCaller, + bool alreadyInlined) { + if (!isFunctionSelectedForPrinting(caller)) + return; + llvm::dbgs() << " " << passName + << (alreadyInlined ? " has inlined " : " will inline ") + << callee->getName() << " into " << caller->getName() << ".\n"; + auto *printee = isCaller ? caller : callee; + printee->dump(caller->getModule().getOptions().EmitVerboseSIL); + llvm::dbgs() << '\n'; +} + +static void printInliningDetailsCallee(StringRef passName, SILFunction *caller, + SILFunction *callee) { + printInliningDetails(passName, caller, callee, /*isCaller=*/false, + /*alreadyInlined=*/false); +} + +static void printInliningDetailsCallerBefore(StringRef passName, + SILFunction *caller, + SILFunction *callee) { + printInliningDetails(passName, caller, callee, /*isCaller=*/true, + /*alreadyInlined=*/false); +} + +static void printInliningDetailsCallerAfter(StringRef passName, + SILFunction *caller, + SILFunction *callee) { + printInliningDetails(passName, caller, callee, /*isCaller=*/true, + /*alreadyInlined=*/true); +} + //===----------------------------------------------------------------------===// // Performance Inliner //===----------------------------------------------------------------------===// @@ -58,6 +111,7 @@ namespace { using Weight = ShortestPathAnalysis::Weight; class SILPerformanceInliner { + StringRef PassName; SILOptFunctionBuilder &FuncBuilder; /// Specifies which functions not to inline, based on @_semantics and @@ -190,12 +244,13 @@ class SILPerformanceInliner { SmallVectorImpl &Applies); public: - SILPerformanceInliner(SILOptFunctionBuilder &FuncBuilder, - InlineSelection WhatToInline, DominanceAnalysis *DA, + SILPerformanceInliner(StringRef PassName, SILOptFunctionBuilder &FuncBuilder, + InlineSelection WhatToInline, DominanceAnalysis *DA, SILLoopAnalysis *LA, SideEffectAnalysis *SEA, OptimizationMode OptMode, OptRemark::Emitter &ORE) - : FuncBuilder(FuncBuilder), WhatToInline(WhatToInline), DA(DA), LA(LA), - SEA(SEA), CBI(DA), ORE(ORE), OptMode(OptMode) {} + : PassName(PassName), FuncBuilder(FuncBuilder), + WhatToInline(WhatToInline), DA(DA), LA(LA), SEA(SEA), CBI(DA), ORE(ORE), + OptMode(OptMode) {} bool inlineCallsIntoFunction(SILFunction *F); }; @@ -975,6 +1030,12 @@ bool SILPerformanceInliner::inlineCallsIntoFunction(SILFunction *Caller) { // will be deleted after inlining. invalidatedStackNesting |= SILInliner::invalidatesStackNesting(AI); + if (SILPrintInliningCallee) { + printInliningDetailsCallee(PassName, Caller, Callee); + } + if (SILPrintInliningCallerBefore) { + printInliningDetailsCallerBefore(PassName, Caller, Callee); + } // We've already determined we should be able to inline this, so // unconditionally inline the function. // @@ -983,6 +1044,9 @@ bool SILPerformanceInliner::inlineCallsIntoFunction(SILFunction *Caller) { SILInliner::inlineFullApply(AI, SILInliner::InlineKind::PerformanceInline, FuncBuilder, deleter); ++NumFunctionsInlined; + if (SILPrintInliningCallerAfter) { + printInliningDetailsCallerAfter(PassName, Caller, Callee); + } } deleter.cleanupDeadInstructions(); @@ -1056,8 +1120,9 @@ class SILPerformanceInlinerPass : public SILFunctionTransform { auto OptMode = getFunction()->getEffectiveOptimizationMode(); SILOptFunctionBuilder FuncBuilder(*this); - SILPerformanceInliner Inliner(FuncBuilder, WhatToInline, DA, LA, SEA, - OptMode, ORE); + + SILPerformanceInliner Inliner(getID(), FuncBuilder, WhatToInline, DA, LA, + SEA, OptMode, ORE); assert(getFunction()->isDefinition() && "Expected only functions with bodies!"); From 6fb5c3042b86cee869a45be207569e1e6df7e680 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Tue, 15 Mar 2022 13:14:44 -0700 Subject: [PATCH 093/242] [CSDiagnostics] Contextual failure could result in optional chain having non-optional type Fixes a crash during diagnostics by not assuming that optional chain would always produce an optional type, which is not true because in error scenarios it could get assigned an invalid type from context. Resolves: rdar://85516390 --- lib/Sema/CSDiagnostics.cpp | 4 +++- .../rdar85516390.swift | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 validation-test/Sema/type_checker_crashers_fixed/rdar85516390.swift diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index e8d1ae96e060f..0d75926af451b 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -2351,7 +2351,9 @@ bool ContextualFailure::diagnoseAsError() { if (isExpr(anchor) || isExpr(anchor)) { auto objectType = fromType->getOptionalObjectType(); - if (objectType->isEqual(toType)) { + // Cannot assume that `fromType` is always optional here since + // it could assume a type from context. + if (objectType && objectType->isEqual(toType)) { MissingOptionalUnwrapFailure failure(getSolution(), getType(anchor), toType, getConstraintLocator(anchor)); diff --git a/validation-test/Sema/type_checker_crashers_fixed/rdar85516390.swift b/validation-test/Sema/type_checker_crashers_fixed/rdar85516390.swift new file mode 100644 index 0000000000000..67ce2dd4874d6 --- /dev/null +++ b/validation-test/Sema/type_checker_crashers_fixed/rdar85516390.swift @@ -0,0 +1,18 @@ +// RUN: %target-typecheck-verify-swift + +protocol P { +} + +struct MyThing : P { + var myVar: String? { "" } +} + +struct Test { + func test(thing: MyThing?) -> P { + return thing?.myVar + // expected-error@-1 {{return expression of type 'String' does not conform to 'P'}} + // expected-error@-2 {{value of optional type 'String?' must be unwrapped to a value of type 'String'}} + // expected-note@-3 {{coalesce using '??' to provide a default when the optional value contains 'nil'}} + // expected-note@-4 {{force-unwrap using '!' to abort execution if the optional value contains 'nil'}} + } +} From b1b9e07f79279c6791c5b79f264305358cc5ad2e Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Tue, 15 Mar 2022 13:34:41 -0700 Subject: [PATCH 094/242] [Swift Static Mirror] Report mangled names without a prefix Leave it to the clients to add it, if necessary. Resolves rdar://90040835 --- include/swift/Reflection/TypeRefBuilder.h | 3 +-- stdlib/public/Reflection/TypeRefBuilder.cpp | 10 ++++------ test/Reflection/conformance_descriptors.swift | 12 ++++++------ 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/include/swift/Reflection/TypeRefBuilder.h b/include/swift/Reflection/TypeRefBuilder.h index 8a82352edbc04..3c2709a73c783 100644 --- a/include/swift/Reflection/TypeRefBuilder.h +++ b/include/swift/Reflection/TypeRefBuilder.h @@ -1350,8 +1350,7 @@ class TypeRefBuilder { auto TypeName = nodeToString(demangleTypeRef(TypeRef)); clearNodeFactory(); if (OptionalMangledTypeName.hasValue()) { - typeNameToManglingMap[TypeName] = - "$s" + OptionalMangledTypeName.getValue(); + typeNameToManglingMap[TypeName] = OptionalMangledTypeName.getValue(); } } } diff --git a/stdlib/public/Reflection/TypeRefBuilder.cpp b/stdlib/public/Reflection/TypeRefBuilder.cpp index 41df014d3db42..be747cec43baa 100644 --- a/stdlib/public/Reflection/TypeRefBuilder.cpp +++ b/stdlib/public/Reflection/TypeRefBuilder.cpp @@ -443,7 +443,7 @@ FieldTypeCollectionResult TypeRefBuilder::collectFieldTypes( clearNodeFactory(); if (optionalMangledTypeName.hasValue()) { auto mangledTypeName = - optionalMangledTypeName.getValue().insert(0, "$s"); + optionalMangledTypeName.getValue(); if (forMangledTypeName.hasValue()) { if (mangledTypeName != forMangledTypeName.getValue()) continue; @@ -461,8 +461,7 @@ FieldTypeCollectionResult TypeRefBuilder::collectFieldTypes( auto optionalMangledfieldTypeName = normalizeReflectionName(fieldTypeRef); if (optionalMangledfieldTypeName.hasValue()) { - mangledFieldTypeName = - "$s" + optionalMangledfieldTypeName.getValue(); + mangledFieldTypeName = optionalMangledfieldTypeName.getValue(); } auto fieldTypeDemangleTree = demangleTypeRef(fieldTypeRef); auto fieldTypeName = nodeToString(fieldTypeDemangleTree); @@ -518,7 +517,7 @@ AssociatedTypeCollectionResult TypeRefBuilder::collectAssociatedTypes( clearNodeFactory(); if (optionalMangledTypeName.hasValue()) { auto mangledTypeName = - optionalMangledTypeName.getValue().insert(0, "$s"); + optionalMangledTypeName.getValue(); if (forMangledTypeName.hasValue()) { if (mangledTypeName != forMangledTypeName.getValue()) continue; @@ -538,8 +537,7 @@ AssociatedTypeCollectionResult TypeRefBuilder::collectAssociatedTypes( auto optionalMangledSubstitutedTypeName = normalizeReflectionName(substitutedTypeRef); if (optionalMangledSubstitutedTypeName.hasValue()) { - mangledSubstitutedTypeName = - "$s" + optionalMangledSubstitutedTypeName.getValue(); + mangledSubstitutedTypeName = optionalMangledSubstitutedTypeName.getValue(); } auto substitutedDemangleTree = demangleTypeRef(substitutedTypeRef); auto substitutedTypeName = nodeToString(substitutedDemangleTree); diff --git a/test/Reflection/conformance_descriptors.swift b/test/Reflection/conformance_descriptors.swift index a2690a61272f9..039d35e1cb241 100644 --- a/test/Reflection/conformance_descriptors.swift +++ b/test/Reflection/conformance_descriptors.swift @@ -13,9 +13,9 @@ // CHECK: CONFORMANCES: // CHECK: ============= -// CHECK-DAG: $s16ConformanceCheck3fooV3barV3bazV3quxV4quuxV5corgeV6graultV6garplyV5waldoV4fredV5plughV5xyzzyV4thudV18SomeConformingTypeV (ConformanceCheck.foo.bar.baz.qux.quux.corge.grault.garply.waldo.fred.plugh.xyzzy.thud.SomeConformingType) : ConformanceCheck.MyProto -// CHECK-DAG: $s16ConformanceCheck7StructAV (ConformanceCheck.StructA) : ConformanceCheck.MyProto, Swift.Hashable, Swift.Equatable -// CHECK-DAG: $s16ConformanceCheck2E4O (ConformanceCheck.E4) : ConformanceCheck.P1, ConformanceCheck.P2, ConformanceCheck.P3 -// CHECK-DAG: $s16ConformanceCheck2C4V (ConformanceCheck.C4) : ConformanceCheck.P1, ConformanceCheck.P2 -// CHECK-DAG: $s16ConformanceCheck2S4V (ConformanceCheck.S4) : ConformanceCheck.P1, ConformanceCheck.P2 -// CHECK-DAG: $s16ConformanceCheck2C1C (ConformanceCheck.C1) : ConformanceCheck.ClassBoundP +// CHECK-DAG: 16ConformanceCheck3fooV3barV3bazV3quxV4quuxV5corgeV6graultV6garplyV5waldoV4fredV5plughV5xyzzyV4thudV18SomeConformingTypeV (ConformanceCheck.foo.bar.baz.qux.quux.corge.grault.garply.waldo.fred.plugh.xyzzy.thud.SomeConformingType) : ConformanceCheck.MyProto +// CHECK-DAG: 16ConformanceCheck7StructAV (ConformanceCheck.StructA) : ConformanceCheck.MyProto, Swift.Hashable, Swift.Equatable +// CHECK-DAG: 16ConformanceCheck2E4O (ConformanceCheck.E4) : ConformanceCheck.P1, ConformanceCheck.P2, ConformanceCheck.P3 +// CHECK-DAG: 16ConformanceCheck2C4V (ConformanceCheck.C4) : ConformanceCheck.P1, ConformanceCheck.P2 +// CHECK-DAG: 16ConformanceCheck2S4V (ConformanceCheck.S4) : ConformanceCheck.P1, ConformanceCheck.P2 +// CHECK-DAG: 16ConformanceCheck2C1C (ConformanceCheck.C1) : ConformanceCheck.ClassBoundP From 83d9b68b8e5e1640aaf4b5951947a889018431df Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 15 Mar 2022 15:14:41 -0700 Subject: [PATCH 095/242] Separate out Duration-based APIs so they aren't in the back-deployment libs The back-deployed Swift Concurrency library should not contain anything based on clocks or durations, which aren't always available in the underlying system. Move that functionality to separate files that are excluded from the back-deployed concurrency libraries. This is a partial step to better approximate the back-deployment libraries. At some point, we'll stop building the back-deployment libraries entirely and instead use the binaries provided by the toolchain. Fixes rdar://89237163. --- .../BackDeployConcurrency/CMakeLists.txt | 7 + stdlib/public/Concurrency/CMakeLists.txt | 11 +- stdlib/public/Concurrency/TaskSleep.swift | 137 +---------------- .../Concurrency/TaskSleepDuration.swift | 145 ++++++++++++++++++ 4 files changed, 162 insertions(+), 138 deletions(-) create mode 100644 stdlib/public/Concurrency/TaskSleepDuration.swift diff --git a/stdlib/public/BackDeployConcurrency/CMakeLists.txt b/stdlib/public/BackDeployConcurrency/CMakeLists.txt index 3ad21efd65310..afcf6525460c6 100644 --- a/stdlib/public/BackDeployConcurrency/CMakeLists.txt +++ b/stdlib/public/BackDeployConcurrency/CMakeLists.txt @@ -58,4 +58,11 @@ set(swift_concurrency_extra_sources "../stubs/SwiftNativeNSObject.mm") set(swift_concurrency_async_fp_mode "never") +set(LLVM_OPTIONAL_SOURCES + Clock.cpp + Clock.swift + ContinuousClock.swift + SuspendingClock.swift + TaskSleepDuration.swift) + add_subdirectory(../Concurrency stdlib/public/BackDeployConcurrency) diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index fb5077cad37b8..057322d6baff4 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -11,7 +11,12 @@ #===----------------------------------------------------------------------===# if(NOT swift_concurrency_extra_sources) - set(swift_concurrency_extra_sources) + set(swift_concurrency_extra_sources + Clock.cpp + Clock.swift + ContinuousClock.swift + SuspendingClock.swift + TaskSleepDuration.swift) endif() if(NOT swift_concurrency_install_component) @@ -113,10 +118,6 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I AsyncThrowingStream.swift AsyncStream.cpp Deque.swift - Clock.cpp - Clock.swift - ContinuousClock.swift - SuspendingClock.swift ${swift_concurrency_extra_sources} linker-support/magic-symbols-for-install-name.c diff --git a/stdlib/public/Concurrency/TaskSleep.swift b/stdlib/public/Concurrency/TaskSleep.swift index 665f30135373f..f491139f5c1f0 100644 --- a/stdlib/public/Concurrency/TaskSleep.swift +++ b/stdlib/public/Concurrency/TaskSleep.swift @@ -31,10 +31,10 @@ extension Task where Success == Never, Failure == Never { /// The type of continuation used in the implementation of /// sleep(nanoseconds:). - private typealias SleepContinuation = UnsafeContinuation<(), Error> + typealias SleepContinuation = UnsafeContinuation<(), Error> /// Describes the state of a sleep() operation. - private enum SleepState { + enum SleepState { /// The sleep continuation has not yet begun. case notStarted @@ -106,7 +106,7 @@ extension Task where Success == Never, Failure == Never { /// Called when the sleep(nanoseconds:) operation woke up without being /// canceled. - private static func onSleepWake( + static func onSleepWake( _ wordPtr: UnsafeMutablePointer ) { while true { @@ -150,7 +150,7 @@ extension Task where Success == Never, Failure == Never { /// Called when the sleep(nanoseconds:) operation has been canceled before /// the sleep completed. - private static func onSleepCancel( + static func onSleepCancel( _ wordPtr: UnsafeMutablePointer ) { while true { @@ -294,133 +294,4 @@ extension Task where Success == Never, Failure == Never { throw error } } - - @available(SwiftStdlib 5.7, *) - internal static func _sleep( - until seconds: Int64, _ nanoseconds: Int64, - tolerance: Duration?, - clock: _ClockID - ) async throws { - // Allocate storage for the storage word. - let wordPtr = UnsafeMutablePointer.allocate(capacity: 1) - - // Initialize the flag word to "not started", which means the continuation - // has neither been created nor completed. - Builtin.atomicstore_seqcst_Word( - wordPtr._rawValue, SleepState.notStarted.word._builtinWordValue) - - do { - // Install a cancellation handler to resume the continuation by - // throwing CancellationError. - try await withTaskCancellationHandler { - let _: () = try await withUnsafeThrowingContinuation { continuation in - while true { - let state = SleepState(loading: wordPtr) - switch state { - case .notStarted: - // The word that describes the active continuation state. - let continuationWord = - SleepState.activeContinuation(continuation).word - - // Try to swap in the continuation word. - let (_, won) = Builtin.cmpxchg_seqcst_seqcst_Word( - wordPtr._rawValue, - state.word._builtinWordValue, - continuationWord._builtinWordValue) - if !Bool(_builtinBooleanLiteral: won) { - // Keep trying! - continue - } - - // Create a task that resumes the continuation normally if it - // finishes first. Enqueue it directly with the delay, so it fires - // when we're done sleeping. - let sleepTaskFlags = taskCreateFlags( - priority: nil, isChildTask: false, copyTaskLocals: false, - inheritContext: false, enqueueJob: false, - addPendingGroupTaskUnconditionally: false) - let (sleepTask, _) = Builtin.createAsyncTask(sleepTaskFlags) { - onSleepWake(wordPtr) - } - let toleranceSeconds: Int64 - let toleranceNanoseconds: Int64 - if let components = tolerance?.components { - toleranceSeconds = components.seconds - toleranceNanoseconds = components.attoseconds / 1_000_000_000 - } else { - toleranceSeconds = 0 - toleranceNanoseconds = -1 - } - - _enqueueJobGlobalWithDeadline( - seconds, nanoseconds, - toleranceSeconds, toleranceNanoseconds, - clock.rawValue, Builtin.convertTaskToJob(sleepTask)) - return - - case .activeContinuation, .finished: - fatalError("Impossible to have multiple active continuations") - - case .cancelled: - fatalError("Impossible to have cancelled before we began") - - case .cancelledBeforeStarted: - // Finish the continuation normally. We'll throw later, after - // we clean up. - continuation.resume() - return - } - } - } - } onCancel: { - onSleepCancel(wordPtr) - } - - // Determine whether we got cancelled before we even started. - let cancelledBeforeStarted: Bool - switch SleepState(loading: wordPtr) { - case .notStarted, .activeContinuation, .cancelled: - fatalError("Invalid state for non-cancelled sleep task") - - case .cancelledBeforeStarted: - cancelledBeforeStarted = true - - case .finished: - cancelledBeforeStarted = false - } - - // We got here without being cancelled, so deallocate the storage for - // the flag word and continuation. - wordPtr.deallocate() - - // If we got cancelled before we even started, through the cancellation - // error now. - if cancelledBeforeStarted { - throw _Concurrency.CancellationError() - } - } catch { - // The task was cancelled; propagate the error. The "on wake" task is - // responsible for deallocating the flag word and continuation, if it's - // still running. - throw error - } - } - - /// Suspends the current task until the given deadline within a tolerance. - /// - /// If the task is canceled before the time ends, this function throws - /// `CancellationError`. - /// - /// This function doesn't block the underlying thread. - /// - /// try await Task.sleep(until: .now + .seconds(3), clock: .continuous) - /// - @available(SwiftStdlib 5.7, *) - public static func sleep( - until deadline: C.Instant, - tolerance: C.Instant.Duration? = nil, - clock: C - ) async throws { - try await clock.sleep(until: deadline, tolerance: tolerance) - } } diff --git a/stdlib/public/Concurrency/TaskSleepDuration.swift b/stdlib/public/Concurrency/TaskSleepDuration.swift new file mode 100644 index 0000000000000..3f20686dc58ce --- /dev/null +++ b/stdlib/public/Concurrency/TaskSleepDuration.swift @@ -0,0 +1,145 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +import Swift +@_implementationOnly import _SwiftConcurrencyShims + +@available(SwiftStdlib 5.7, *) +extension Task where Success == Never, Failure == Never { + @available(SwiftStdlib 5.7, *) + internal static func _sleep( + until seconds: Int64, _ nanoseconds: Int64, + tolerance: Duration?, + clock: _ClockID + ) async throws { + // Allocate storage for the storage word. + let wordPtr = UnsafeMutablePointer.allocate(capacity: 1) + + // Initialize the flag word to "not started", which means the continuation + // has neither been created nor completed. + Builtin.atomicstore_seqcst_Word( + wordPtr._rawValue, SleepState.notStarted.word._builtinWordValue) + + do { + // Install a cancellation handler to resume the continuation by + // throwing CancellationError. + try await withTaskCancellationHandler { + let _: () = try await withUnsafeThrowingContinuation { continuation in + while true { + let state = SleepState(loading: wordPtr) + switch state { + case .notStarted: + // The word that describes the active continuation state. + let continuationWord = + SleepState.activeContinuation(continuation).word + + // Try to swap in the continuation word. + let (_, won) = Builtin.cmpxchg_seqcst_seqcst_Word( + wordPtr._rawValue, + state.word._builtinWordValue, + continuationWord._builtinWordValue) + if !Bool(_builtinBooleanLiteral: won) { + // Keep trying! + continue + } + + // Create a task that resumes the continuation normally if it + // finishes first. Enqueue it directly with the delay, so it fires + // when we're done sleeping. + let sleepTaskFlags = taskCreateFlags( + priority: nil, isChildTask: false, copyTaskLocals: false, + inheritContext: false, enqueueJob: false, + addPendingGroupTaskUnconditionally: false) + let (sleepTask, _) = Builtin.createAsyncTask(sleepTaskFlags) { + onSleepWake(wordPtr) + } + let toleranceSeconds: Int64 + let toleranceNanoseconds: Int64 + if let components = tolerance?.components { + toleranceSeconds = components.seconds + toleranceNanoseconds = components.attoseconds / 1_000_000_000 + } else { + toleranceSeconds = 0 + toleranceNanoseconds = -1 + } + + _enqueueJobGlobalWithDeadline( + seconds, nanoseconds, + toleranceSeconds, toleranceNanoseconds, + clock.rawValue, Builtin.convertTaskToJob(sleepTask)) + return + + case .activeContinuation, .finished: + fatalError("Impossible to have multiple active continuations") + + case .cancelled: + fatalError("Impossible to have cancelled before we began") + + case .cancelledBeforeStarted: + // Finish the continuation normally. We'll throw later, after + // we clean up. + continuation.resume() + return + } + } + } + } onCancel: { + onSleepCancel(wordPtr) + } + + // Determine whether we got cancelled before we even started. + let cancelledBeforeStarted: Bool + switch SleepState(loading: wordPtr) { + case .notStarted, .activeContinuation, .cancelled: + fatalError("Invalid state for non-cancelled sleep task") + + case .cancelledBeforeStarted: + cancelledBeforeStarted = true + + case .finished: + cancelledBeforeStarted = false + } + + // We got here without being cancelled, so deallocate the storage for + // the flag word and continuation. + wordPtr.deallocate() + + // If we got cancelled before we even started, through the cancellation + // error now. + if cancelledBeforeStarted { + throw _Concurrency.CancellationError() + } + } catch { + // The task was cancelled; propagate the error. The "on wake" task is + // responsible for deallocating the flag word and continuation, if it's + // still running. + throw error + } + } + + /// Suspends the current task until the given deadline within a tolerance. + /// + /// If the task is canceled before the time ends, this function throws + /// `CancellationError`. + /// + /// This function doesn't block the underlying thread. + /// + /// try await Task.sleep(until: .now + .seconds(3), clock: .continuous) + /// + @available(SwiftStdlib 5.7, *) + public static func sleep( + until deadine: C.Instant, + tolerance: C.Instant.Duration? = nil, + clock: C + ) async throws { + try await clock.sleep(until: deadine, tolerance: tolerance) + } +} From 41971284faac56502fd59267477f89fbba36d662 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Tue, 15 Mar 2022 15:18:00 -0700 Subject: [PATCH 096/242] Fix variable name --- stdlib/public/Concurrency/TaskSleepDuration.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/Concurrency/TaskSleepDuration.swift b/stdlib/public/Concurrency/TaskSleepDuration.swift index 3f20686dc58ce..9c28eaf108582 100644 --- a/stdlib/public/Concurrency/TaskSleepDuration.swift +++ b/stdlib/public/Concurrency/TaskSleepDuration.swift @@ -136,10 +136,10 @@ extension Task where Success == Never, Failure == Never { /// @available(SwiftStdlib 5.7, *) public static func sleep( - until deadine: C.Instant, + until deadline: C.Instant, tolerance: C.Instant.Duration? = nil, clock: C ) async throws { - try await clock.sleep(until: deadine, tolerance: tolerance) + try await clock.sleep(until: deadline, tolerance: tolerance) } } From fe6cefc63c61dae6f77c407cc833ac7dba52e239 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 15 Mar 2022 14:28:55 -0700 Subject: [PATCH 097/242] Downgrade To A Warning --- lib/Sema/TypeCheckPattern.cpp | 7 ++++-- test/Constraints/result_builder_diags.swift | 27 +++++++++++++++++++++ test/stmt/switch_nil.swift | 3 +-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/lib/Sema/TypeCheckPattern.cpp b/lib/Sema/TypeCheckPattern.cpp index 8e848941cf60c..65e35293f703a 100644 --- a/lib/Sema/TypeCheckPattern.cpp +++ b/lib/Sema/TypeCheckPattern.cpp @@ -1294,8 +1294,11 @@ Pattern *TypeChecker::coercePatternToType(ContextualPattern pattern, } else { // ...but for non-optional types it can never match! Diagnose it. diags.diagnose(NLE->getLoc(), - diag::value_type_comparison_with_nil_illegal, type); - return nullptr; + diag::value_type_comparison_with_nil_illegal, type) + .warnUntilSwiftVersion(6); + + if (type->getASTContext().isSwiftVersionAtLeast(6)) + return nullptr; } } diff --git a/test/Constraints/result_builder_diags.swift b/test/Constraints/result_builder_diags.swift index ce189a976d472..47b36a3065d7a 100644 --- a/test/Constraints/result_builder_diags.swift +++ b/test/Constraints/result_builder_diags.swift @@ -826,3 +826,30 @@ func test_redeclations() { let (foo, foo) = (5, 6) // expected-error {{invalid redeclaration of 'foo'}} expected-note {{'foo' previously declared here}} } } + +func test_rdar89742267() { + @resultBuilder + struct Builder { + static func buildBlock(_ t: T) -> T { t } + static func buildEither(first: T) -> T { first } + static func buildEither(second: T) -> T { second } + } + + struct S {} + + enum Hey { + case listen + } + + struct MyView { + var entry: Hey + + @Builder var body: S { + switch entry { + case .listen: S() + case nil: S() // expected-warning {{type 'Hey' is not optional, value can never be nil; this is an error in Swift 6}} + default: S() + } + } + } +} diff --git a/test/stmt/switch_nil.swift b/test/stmt/switch_nil.swift index ba642824448c7..019d00d655e56 100644 --- a/test/stmt/switch_nil.swift +++ b/test/stmt/switch_nil.swift @@ -6,10 +6,9 @@ enum Hey { func test() { switch Hey.listen { - case nil: // expected-error{{type 'Hey' is not optional, value can never be nil}} + case nil: // expected-warning {{type 'Hey' is not optional, value can never be nil}} break default: break } } - From 1f125c3b5b22ae64bd4b3fa57befa4c217ada026 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 22:38:44 -0400 Subject: [PATCH 098/242] RequirementMachine: Make some methods on RuleBuilder private --- lib/AST/RequirementMachine/RequirementLowering.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.h b/lib/AST/RequirementMachine/RequirementLowering.h index f1aff17bf23c3..db211576a983b 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.h +++ b/lib/AST/RequirementMachine/RequirementLowering.h @@ -125,6 +125,9 @@ struct RuleBuilder { void addProtocols(ArrayRef proto); void addProtocol(const ProtocolDecl *proto, bool initialComponent); + void collectRulesFromReferencedProtocols(); + +private: void addAssociatedType(const AssociatedTypeDecl *type, const ProtocolDecl *proto); void addRequirement(const Requirement &req, @@ -134,7 +137,6 @@ struct RuleBuilder { const ProtocolDecl *proto); void addTypeAlias(const ProtocolTypeAlias &alias, const ProtocolDecl *proto); - void collectRulesFromReferencedProtocols(); }; // Defined in ConcreteContraction.cpp. From 1d8dd945015bd1b81ce3850e2c5e76ae3ff88a9a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 23:01:04 -0400 Subject: [PATCH 099/242] RequirementMachine: Refactor RuleBuilder in preparation for rule sharing --- .../ConcreteTypeWitness.cpp | 4 +- .../RequirementLowering.cpp | 133 ++++++++++-------- .../RequirementMachine/RequirementLowering.h | 40 +++--- .../RequirementMachine/RequirementMachine.cpp | 6 +- lib/AST/RequirementMachine/RewriteSystem.h | 23 ++- 5 files changed, 112 insertions(+), 94 deletions(-) diff --git a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp index 2edb6a0f88547..58845072df9f1 100644 --- a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp +++ b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp @@ -562,8 +562,8 @@ void PropertyMap::inferConditionalRequirements( llvm::dbgs() << "@@@ Unknown protocol: "<< proto->getName() << "\n"; } - RuleBuilder builder(Context, System.getProtocolMap()); - builder.addProtocol(proto, /*initialComponent=*/false); + RuleBuilder builder(Context, System.getReferencedProtocols()); + builder.addReferencedProtocol(proto); builder.collectRulesFromReferencedProtocols(); for (const auto &rule : builder.PermanentRules) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 0a5cd9b8a130b..3ad582f1c2b55 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -1051,7 +1051,7 @@ void RuleBuilder::addRequirements(ArrayRef requirements) { // Collect all protocols transitively referenced from these requirements. for (auto req : requirements) { if (req.getKind() == RequirementKind::Conformance) { - addProtocol(req.getProtocolDecl(), /*initialComponent=*/false); + addReferencedProtocol(req.getProtocolDecl()); } } @@ -1066,7 +1066,7 @@ void RuleBuilder::addRequirements(ArrayRef requirements) // Collect all protocols transitively referenced from these requirements. for (auto req : requirements) { if (req.req.getKind() == RequirementKind::Conformance) { - addProtocol(req.req.getProtocolDecl(), /*initialComponent=*/false); + addReferencedProtocol(req.req.getProtocolDecl()); } } @@ -1077,16 +1077,65 @@ void RuleBuilder::addRequirements(ArrayRef requirements) addRequirement(req, /*proto=*/nullptr); } +/// For building a rewrite system for a protocol connected component from +/// user-written requirements. Used when actually building requirement +/// signatures. void RuleBuilder::addProtocols(ArrayRef protos) { - // Collect all protocols transitively referenced from this connected component - // of the protocol dependency graph. - for (auto proto : protos) { - addProtocol(proto, /*initialComponent=*/true); + for (auto *proto : protos) { + ReferencedProtocols.insert(proto); + } + + for (auto *proto : protos) { + if (Dump) { + llvm::dbgs() << "protocol " << proto->getName() << " {\n"; + } + + addPermanentProtocolRules(proto); + + for (auto req : proto->getStructuralRequirements()) + addRequirement(req, proto); + + for (auto req : proto->getTypeAliasRequirements()) + addRequirement(req.getCanonical(), proto, /*requirementID=*/None); + + for (auto *otherProto : proto->getProtocolDependencies()) + addReferencedProtocol(otherProto); + + if (Dump) { + llvm::dbgs() << "}\n"; + } } + // Collect all protocols transitively referenced from this connected component + // of the protocol dependency graph. collectRulesFromReferencedProtocols(); } +/// Add permanent rules for a protocol, consisting of: +/// +/// - The identity conformance rule [P].[P] => [P]. +/// - An associated type introduction rule for each associated type. +/// - An inherited associated type introduction rule for each associated +/// type of each inherited protocol. +void RuleBuilder::addPermanentProtocolRules(const ProtocolDecl *proto) { + MutableTerm lhs; + lhs.add(Symbol::forProtocol(proto, Context)); + lhs.add(Symbol::forProtocol(proto, Context)); + + MutableTerm rhs; + rhs.add(Symbol::forProtocol(proto, Context)); + + PermanentRules.emplace_back(lhs, rhs); + + for (auto *assocType : proto->getAssociatedTypeMembers()) + addAssociatedType(assocType, proto); + + for (auto *inheritedProto : Context.getInheritedProtocols(proto)) { + for (auto *assocType : inheritedProto->getAssociatedTypeMembers()) + addAssociatedType(assocType, proto); + } +} + /// For an associated type T in a protocol P, we add a rewrite rule: /// /// [P].T => [P:T] @@ -1264,72 +1313,44 @@ void RuleBuilder::addTypeAlias(const ProtocolTypeAlias &alias, /*requirementID=*/None); } -/// Record information about a protocol if we have no seen it yet. -void RuleBuilder::addProtocol(const ProtocolDecl *proto, - bool initialComponent) { - if (ProtocolMap.count(proto) > 0) - return; - - ProtocolMap[proto] = initialComponent; - Protocols.push_back(proto); +/// If we haven't seen this protocol yet, save it for later so that we can +/// import the rewrite rules from its connected component. +void RuleBuilder::addReferencedProtocol(const ProtocolDecl *proto) { + if (ReferencedProtocols.insert(proto).second) + ProtocolsToImport.push_back(proto); } /// Compute the transitive closure of the set of all protocols referenced from /// the right hand sides of conformance requirements, and convert their /// requirements to rewrite rules. void RuleBuilder::collectRulesFromReferencedProtocols() { + // Compute the transitive closure. unsigned i = 0; - while (i < Protocols.size()) { - auto *proto = Protocols[i++]; + while (i < ProtocolsToImport.size()) { + auto *proto = ProtocolsToImport[i++]; for (auto *depProto : proto->getProtocolDependencies()) { - addProtocol(depProto, /*initialComponent=*/false); + addReferencedProtocol(depProto); } } - // Add rewrite rules for each protocol. - for (auto *proto : Protocols) { + // If this is a rewrite system for a generic signature, add rewrite rules for + // each referenced protocol. + // + // if this is a rewrite system for a connected component of the protocol + // dependency graph, add rewrite rules for each referenced protocol not part + // of this connected component. + for (auto *proto : ProtocolsToImport) { if (Dump) { llvm::dbgs() << "protocol " << proto->getName() << " {\n"; } - // Add the identity conformance rule [P].[P] => [P]. - MutableTerm lhs; - lhs.add(Symbol::forProtocol(proto, Context)); - lhs.add(Symbol::forProtocol(proto, Context)); - - MutableTerm rhs; - rhs.add(Symbol::forProtocol(proto, Context)); - - PermanentRules.emplace_back(lhs, rhs); - - for (auto *assocType : proto->getAssociatedTypeMembers()) - addAssociatedType(assocType, proto); - - for (auto *inheritedProto : Context.getInheritedProtocols(proto)) { - for (auto *assocType : inheritedProto->getAssociatedTypeMembers()) - addAssociatedType(assocType, proto); - } + addPermanentProtocolRules(proto); - // If this protocol is part of the initial connected component, we're - // building requirement signatures for all protocols in this component, - // and so we must start with the structural requirements. - // - // Otherwise, we should either already have a requirement signature, or - // we can trigger the computation of the requirement signatures of the - // next component recursively. - if (ProtocolMap[proto]) { - for (auto req : proto->getStructuralRequirements()) - addRequirement(req, proto); - - for (auto req : proto->getTypeAliasRequirements()) - addRequirement(req.getCanonical(), proto, /*requirementID=*/None); - } else { - auto reqs = proto->getRequirementSignature(); - for (auto req : reqs.getRequirements()) - addRequirement(req.getCanonical(), proto, /*requirementID=*/None); - for (auto alias : reqs.getTypeAliases()) - addTypeAlias(alias, proto); - } + auto reqs = proto->getRequirementSignature(); + for (auto req : reqs.getRequirements()) + addRequirement(req.getCanonical(), proto, /*requirementID=*/None); + for (auto alias : reqs.getTypeAliases()) + addTypeAlias(alias, proto); if (Dump) { llvm::dbgs() << "}\n"; diff --git a/lib/AST/RequirementMachine/RequirementLowering.h b/lib/AST/RequirementMachine/RequirementLowering.h index db211576a983b..f7f909839342c 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.h +++ b/lib/AST/RequirementMachine/RequirementLowering.h @@ -15,7 +15,7 @@ #include "swift/AST/Type.h" #include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallVector.h" #include #include "Diagnostics.h" @@ -76,24 +76,22 @@ getRuleForRequirement(const Requirement &req, struct RuleBuilder { RewriteContext &Context; - /// The keys are the unique protocols we've added so far. The value indicates - /// whether the protocol's SCC is an initial component for the rewrite system. - /// - /// A rewrite system built from a generic signature does not have any initial - /// protocols. - /// - /// A rewrite system built from a protocol SCC has the protocols of the SCC - /// itself as initial protocols. + /// The transitive closure of all protocols appearing on the right hand + /// side of conformance requirements. + llvm::DenseSet &ReferencedProtocols; + + /// A subset of the above in insertion order, consisting of the protocols + /// whose rules we are going to import. /// - /// If a protocol is an initial protocol, we use its structural requirements - /// instead of its requirement signature as the basis of its rewrite rules. + /// If this is a rewrite system built from a generic signature, this vector + /// contains all elements in the above set. /// - /// This is what breaks the cycle in requirement signature computation for a - /// group of interdependent protocols. - llvm::DenseMap &ProtocolMap; - - /// The keys of the above map in insertion order. - std::vector Protocols; + /// If this is a rewrite system built from a strongly connected component + /// of the protocol, this vector contains all elements in the above set + /// except for the protocols belonging to the component representing the + /// rewrite system itself; those protocols are added directly instead of + /// being imported. + std::vector ProtocolsToImport; /// New rules to add which will be marked 'permanent'. These are rules for /// introducing associated types, and relationships between layout, @@ -116,18 +114,18 @@ struct RuleBuilder { bool Dump; RuleBuilder(RewriteContext &ctx, - llvm::DenseMap &protocolMap) - : Context(ctx), ProtocolMap(protocolMap), + llvm::DenseSet &referencedProtocols) + : Context(ctx), ReferencedProtocols(referencedProtocols), Dump(ctx.getASTContext().LangOpts.DumpRequirementMachine) {} void addRequirements(ArrayRef requirements); void addRequirements(ArrayRef requirements); void addProtocols(ArrayRef proto); - void addProtocol(const ProtocolDecl *proto, - bool initialComponent); + void addReferencedProtocol(const ProtocolDecl *proto); void collectRulesFromReferencedProtocols(); private: + void addPermanentProtocolRules(const ProtocolDecl *proto); void addAssociatedType(const AssociatedTypeDecl *type, const ProtocolDecl *proto); void addRequirement(const Requirement &req, diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp index c6aa61fcf9a66..389bb1832b41d 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.cpp +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -90,7 +90,7 @@ RequirementMachine::initWithGenericSignature(CanGenericSignature sig) { // Collect the top-level requirements, and all transtively-referenced // protocol requirement signatures. - RuleBuilder builder(Context, System.getProtocolMap()); + RuleBuilder builder(Context, System.getReferencedProtocols()); builder.addRequirements(sig.getRequirements()); // Add the initial set of rewrite rules to the rewrite system. @@ -134,7 +134,7 @@ RequirementMachine::initWithProtocols(ArrayRef protos) { llvm::dbgs() << " {\n"; } - RuleBuilder builder(Context, System.getProtocolMap()); + RuleBuilder builder(Context, System.getReferencedProtocols()); builder.addProtocols(protos); // Add the initial set of rewrite rules to the rewrite system. @@ -181,7 +181,7 @@ RequirementMachine::initWithWrittenRequirements( // Collect the top-level requirements, and all transtively-referenced // protocol requirement signatures. - RuleBuilder builder(Context, System.getProtocolMap()); + RuleBuilder builder(Context, System.getReferencedProtocols()); builder.addRequirements(requirements); // Add the initial set of rewrite rules to the rewrite system. diff --git a/lib/AST/RequirementMachine/RewriteSystem.h b/lib/AST/RequirementMachine/RewriteSystem.h index 42e5303edb3d6..7141b9221e55f 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.h +++ b/lib/AST/RequirementMachine/RewriteSystem.h @@ -76,16 +76,15 @@ class RewriteSystem final { /// type is an index into the Rules array defined above. Trie Trie; - /// The set of protocols known to this rewrite system. The boolean associated - /// with each key is true if the protocol is part of the 'Protos' set above, - /// otherwies it is false. + /// The set of protocols known to this rewrite system. /// - /// See RuleBuilder::ProtocolMap for a more complete explanation. For the most - /// part, this is only used while building the rewrite system, but conditional - /// requirement inference forces us to be able to add new protocols to the - /// rewrite system after the fact, so this little bit of RuleBuilder state - /// outlives the initialization phase. - llvm::DenseMap ProtocolMap; + /// See RuleBuilder::ReferencedProtocols for a more complete explanation. + /// + /// For the most part, this is only used while building the rewrite system, + /// but conditional requirement inference forces us to be able to add new + /// protocols to the rewrite system after the fact, so this little bit of + /// RuleBuilder state outlives the initialization phase. + llvm::DenseSet ReferencedProtocols; DebugOptions Debug; @@ -117,8 +116,8 @@ class RewriteSystem final { /// Return the rewrite context used for allocating memory. RewriteContext &getRewriteContext() const { return Context; } - llvm::DenseMap &getProtocolMap() { - return ProtocolMap; + llvm::DenseSet &getReferencedProtocols() { + return ReferencedProtocols; } DebugOptions getDebugOptions() const { return Debug; } @@ -133,7 +132,7 @@ class RewriteSystem final { } bool isKnownProtocol(const ProtocolDecl *proto) const { - return ProtocolMap.find(proto) != ProtocolMap.end(); + return ReferencedProtocols.count(proto) > 0; } unsigned getRuleID(const Rule &rule) const { From 564f626f622f992709e339ee83e45f4c72adc525 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 23:23:00 -0400 Subject: [PATCH 100/242] RequirementMachine: Rename some RuleBuilder methods for clarity --- .../RequirementLowering.cpp | 22 ++++++++++++++++--- .../RequirementMachine/RequirementLowering.h | 19 ++++++++++------ .../RequirementMachine/RequirementMachine.cpp | 6 ++--- 3 files changed, 34 insertions(+), 13 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 3ad582f1c2b55..ec75761318bd8 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -1047,7 +1047,13 @@ ProtocolDependenciesRequest::evaluate(Evaluator &evaluator, // Building rewrite rules from desugared requirements. // -void RuleBuilder::addRequirements(ArrayRef requirements) { +/// For building a rewrite system for a generic signature from canonical +/// requirements. +void RuleBuilder::initWithGenericSignatureRequirements( + ArrayRef requirements) { + assert(!Initialized); + Initialized = 1; + // Collect all protocols transitively referenced from these requirements. for (auto req : requirements) { if (req.getKind() == RequirementKind::Conformance) { @@ -1062,7 +1068,13 @@ void RuleBuilder::addRequirements(ArrayRef requirements) { addRequirement(req, /*proto=*/nullptr, /*requirementID=*/None); } -void RuleBuilder::addRequirements(ArrayRef requirements) { +/// For building a rewrite system for a generic signature from user-written +/// requirements. +void RuleBuilder::initWithWrittenRequirements( + ArrayRef requirements) { + assert(!Initialized); + Initialized = 1; + // Collect all protocols transitively referenced from these requirements. for (auto req : requirements) { if (req.req.getKind() == RequirementKind::Conformance) { @@ -1080,7 +1092,11 @@ void RuleBuilder::addRequirements(ArrayRef requirements) /// For building a rewrite system for a protocol connected component from /// user-written requirements. Used when actually building requirement /// signatures. -void RuleBuilder::addProtocols(ArrayRef protos) { +void RuleBuilder::initWithProtocolWrittenRequirements( + ArrayRef protos) { + assert(!Initialized); + Initialized = 1; + for (auto *proto : protos) { ReferencedProtocols.insert(proto); } diff --git a/lib/AST/RequirementMachine/RequirementLowering.h b/lib/AST/RequirementMachine/RequirementLowering.h index f7f909839342c..415deb6d34fde 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.h +++ b/lib/AST/RequirementMachine/RequirementLowering.h @@ -111,16 +111,21 @@ struct RuleBuilder { /// Enables debugging output. Controlled by the -dump-requirement-machine /// frontend flag. - bool Dump; + unsigned Dump : 1; + + /// Used to ensure the initWith*() methods are only called once. + unsigned Initialized : 1; RuleBuilder(RewriteContext &ctx, llvm::DenseSet &referencedProtocols) - : Context(ctx), ReferencedProtocols(referencedProtocols), - Dump(ctx.getASTContext().LangOpts.DumpRequirementMachine) {} - - void addRequirements(ArrayRef requirements); - void addRequirements(ArrayRef requirements); - void addProtocols(ArrayRef proto); + : Context(ctx), ReferencedProtocols(referencedProtocols) { + Dump = ctx.getASTContext().LangOpts.DumpRequirementMachine; + Initialized = 0; + } + + void initWithGenericSignatureRequirements(ArrayRef requirements); + void initWithWrittenRequirements(ArrayRef requirements); + void initWithProtocolWrittenRequirements(ArrayRef proto); void addReferencedProtocol(const ProtocolDecl *proto); void collectRulesFromReferencedProtocols(); diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp index 389bb1832b41d..f29982c59d6d3 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.cpp +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -91,7 +91,7 @@ RequirementMachine::initWithGenericSignature(CanGenericSignature sig) { // Collect the top-level requirements, and all transtively-referenced // protocol requirement signatures. RuleBuilder builder(Context, System.getReferencedProtocols()); - builder.addRequirements(sig.getRequirements()); + builder.initWithGenericSignatureRequirements(sig.getRequirements()); // Add the initial set of rewrite rules to the rewrite system. System.initialize(/*recordLoops=*/false, @@ -135,7 +135,7 @@ RequirementMachine::initWithProtocols(ArrayRef protos) { } RuleBuilder builder(Context, System.getReferencedProtocols()); - builder.addProtocols(protos); + builder.initWithProtocolWrittenRequirements(protos); // Add the initial set of rewrite rules to the rewrite system. System.initialize(/*recordLoops=*/true, protos, @@ -182,7 +182,7 @@ RequirementMachine::initWithWrittenRequirements( // Collect the top-level requirements, and all transtively-referenced // protocol requirement signatures. RuleBuilder builder(Context, System.getReferencedProtocols()); - builder.addRequirements(requirements); + builder.initWithWrittenRequirements(requirements); // Add the initial set of rewrite rules to the rewrite system. System.initialize(/*recordLoops=*/true, From f3bcc52e6cc002f8b0fa658c70160bfbf300fcb2 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 23:26:23 -0400 Subject: [PATCH 101/242] RequirementMachine: Rename RequirementMachine::initWithProtocols() --- lib/AST/RequirementMachine/RequirementMachine.cpp | 3 ++- lib/AST/RequirementMachine/RequirementMachine.h | 3 ++- lib/AST/RequirementMachine/RequirementMachineRequests.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp index f29982c59d6d3..54f23703ec7af 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.cpp +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -123,7 +123,8 @@ RequirementMachine::initWithGenericSignature(CanGenericSignature sig) { /// /// Returns failure if completion fails within the configured number of steps. std::pair -RequirementMachine::initWithProtocols(ArrayRef protos) { +RequirementMachine::initWithProtocolWrittenRequirements( + ArrayRef protos) { FrontendStatsTracer tracer(Stats, "build-rewrite-system"); if (Dump) { diff --git a/lib/AST/RequirementMachine/RequirementMachine.h b/lib/AST/RequirementMachine/RequirementMachine.h index 30883259c3c09..786cf741df36c 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.h +++ b/lib/AST/RequirementMachine/RequirementMachine.h @@ -94,7 +94,8 @@ class RequirementMachine final { initWithGenericSignature(CanGenericSignature sig); std::pair - initWithProtocols(ArrayRef protos); + initWithProtocolWrittenRequirements( + ArrayRef protos); std::pair initWithWrittenRequirements( diff --git a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp index 5c2d1d8e33e83..382b78493f647 100644 --- a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp +++ b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp @@ -116,7 +116,7 @@ RequirementSignatureRequestRQM::evaluate(Evaluator &evaluator, SmallVector errors; - auto status = machine->initWithProtocols(component); + auto status = machine->initWithProtocolWrittenRequirements(component); if (status.first != CompletionResult::Success) { // All we can do at this point is diagnose and give each protocol an empty // requirement signature. From aaf84ac6c254261ff2b34a605e60316aa478880a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 23:37:42 -0400 Subject: [PATCH 102/242] RequirementMachine: Implement RequirementMachine::initWithProtocolSignatureRequirements() --- .../RequirementLowering.cpp | 48 ++++++++++++++++++ .../RequirementMachine/RequirementLowering.h | 1 + .../RequirementMachine/RequirementMachine.cpp | 50 +++++++++++++++++-- .../RequirementMachine/RequirementMachine.h | 4 ++ 4 files changed, 98 insertions(+), 5 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index ec75761318bd8..9d34338007133 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -1089,6 +1089,51 @@ void RuleBuilder::initWithWrittenRequirements( addRequirement(req, /*proto=*/nullptr); } +/// For building a rewrite system for a protocol connected component from +/// a previously-built requirement signature. +/// +/// Will trigger requirement signature computation if we haven't built +/// requirement signatures for this connected component yet, in which case we +/// will recursively end up building another rewrite system for this component +/// using initWithProtocolWrittenRequirements(). +void RuleBuilder::initWithProtocolSignatureRequirements( + ArrayRef protos) { + assert(!Initialized); + Initialized = 1; + + // Add all protocols to the referenced set, so that subsequent calls + // to addReferencedProtocol() with one of these protocols don't add + // them to the import list. + for (auto *proto : protos) { + ReferencedProtocols.insert(proto); + } + + for (auto *proto : protos) { + if (Dump) { + llvm::dbgs() << "protocol " << proto->getName() << " {\n"; + } + + addPermanentProtocolRules(proto); + + auto reqs = proto->getRequirementSignature(); + for (auto req : reqs.getRequirements()) + addRequirement(req.getCanonical(), proto, /*requirementID=*/None); + for (auto alias : reqs.getTypeAliases()) + addTypeAlias(alias, proto); + + for (auto *otherProto : proto->getProtocolDependencies()) + addReferencedProtocol(otherProto); + + if (Dump) { + llvm::dbgs() << "}\n"; + } + } + + // Collect all protocols transitively referenced from this connected component + // of the protocol dependency graph. + collectRulesFromReferencedProtocols(); +} + /// For building a rewrite system for a protocol connected component from /// user-written requirements. Used when actually building requirement /// signatures. @@ -1097,6 +1142,9 @@ void RuleBuilder::initWithProtocolWrittenRequirements( assert(!Initialized); Initialized = 1; + // Add all protocols to the referenced set, so that subsequent calls + // to addReferencedProtocol() with one of these protocols don't add + // them to the import list. for (auto *proto : protos) { ReferencedProtocols.insert(proto); } diff --git a/lib/AST/RequirementMachine/RequirementLowering.h b/lib/AST/RequirementMachine/RequirementLowering.h index 415deb6d34fde..d331cf6e7a00e 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.h +++ b/lib/AST/RequirementMachine/RequirementLowering.h @@ -125,6 +125,7 @@ struct RuleBuilder { void initWithGenericSignatureRequirements(ArrayRef requirements); void initWithWrittenRequirements(ArrayRef requirements); + void initWithProtocolSignatureRequirements(ArrayRef proto); void initWithProtocolWrittenRequirements(ArrayRef proto); void addReferencedProtocol(const ProtocolDecl *proto); void collectRulesFromReferencedProtocols(); diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp index 54f23703ec7af..9d7271dde535e 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.cpp +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -63,6 +63,46 @@ void RequirementMachine::checkCompletionResult(CompletionResult result) const { } } +/// Build a requirement machine for the previously-computed requirement +/// signatures connected component of protocols. +/// +/// This must only be called exactly once, before any other operations are +/// performed on this requirement machine. +/// +/// Used by RewriteContext::getRequirementMachine(const ProtocolDecl *). +/// +/// Returns failure if completion fails within the configured number of steps. +std::pair +RequirementMachine::initWithProtocolSignatureRequirements( + ArrayRef protos) { + FrontendStatsTracer tracer(Stats, "build-rewrite-system"); + + if (Dump) { + llvm::dbgs() << "Adding protocols"; + for (auto *proto : protos) { + llvm::dbgs() << " " << proto->getName(); + } + llvm::dbgs() << " {\n"; + } + + RuleBuilder builder(Context, System.getReferencedProtocols()); + builder.initWithProtocolSignatureRequirements(protos); + + // Add the initial set of rewrite rules to the rewrite system. + System.initialize(/*recordLoops=*/true, protos, + std::move(builder.WrittenRequirements), + std::move(builder.PermanentRules), + std::move(builder.RequirementRules)); + + auto result = computeCompletion(RewriteSystem::DisallowInvalidRequirements); + + if (Dump) { + llvm::dbgs() << "}\n"; + } + + return result; +} + /// Build a requirement machine for the requirements of a generic signature. /// /// In this mode, minimization is not going to be performed, so rewrite loops @@ -109,12 +149,12 @@ RequirementMachine::initWithGenericSignature(CanGenericSignature sig) { return result; } -/// Build a requirement machine for the structural requirements of a set -/// of protocols, which are understood to form a strongly-connected component -/// (SCC) of the protocol dependency graph. +/// Build a requirement machine for the user-written requirements of connected +/// component of protocols. /// -/// In this mode, minimization will be performed, so rewrite loops are recorded -/// during completion. +/// This is used when actually building the requirement signatures of these +/// protocols. In this mode, minimization will be performed, so rewrite loops +/// are recorded during completion. /// /// This must only be called exactly once, before any other operations are /// performed on this requirement machine. diff --git a/lib/AST/RequirementMachine/RequirementMachine.h b/lib/AST/RequirementMachine/RequirementMachine.h index 786cf741df36c..f236ffa80d1c3 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.h +++ b/lib/AST/RequirementMachine/RequirementMachine.h @@ -90,6 +90,10 @@ class RequirementMachine final { void checkCompletionResult(CompletionResult result) const; + std::pair + initWithProtocolSignatureRequirements( + ArrayRef protos); + std::pair initWithGenericSignature(CanGenericSignature sig); From c34f7055f83fc72f895a8272f74d619e3eeef2cf Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Sun, 13 Mar 2022 17:47:46 -0700 Subject: [PATCH 103/242] Detach `enable-experimental-async-top-level` flag SE-0343 is approved so it's time to pull the feature out from behind the experimental feature flag. This patch pulls it out and deprecates passing the flag to the frontend so that we can pull it out entirely eventually. --- include/swift/AST/DiagnosticsFrontend.def | 2 ++ include/swift/Basic/LangOptions.h | 3 --- lib/AST/Decl.cpp | 4 +--- lib/AST/DeclContext.cpp | 6 ++---- lib/Frontend/CompilerInvocation.cpp | 9 ++++----- lib/Sema/TypeCheckConcurrency.cpp | 11 +++++------ 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index f470961abdb0c..d9d48be606cb4 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -420,6 +420,8 @@ ERROR(error_nonexistent_output_dir,none, REMARK(interface_file_backup_used,none, "building module from '%0' failed; retrying building module from '%1'", (StringRef, StringRef)) +WARNING(warn_flag_deprecated,none, "flag '%0' is deprecated", (StringRef)) + // Dependency Verifier Diagnostics ERROR(missing_member_dependency,none, "expected " diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 64b0bb776648a..c73be27773a30 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -407,9 +407,6 @@ namespace swift { /// cases. bool EnableNonFrozenEnumExhaustivityDiagnostics = false; - /// Enable making top-level code support concurrency - bool EnableExperimentalAsyncTopLevel = false; - /// Regex for the passes that should report passed and missed optimizations. /// /// These are shared_ptrs so that this class remains copyable. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 576aaff8986d2..5a4f96d6a884d 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -9088,9 +9088,7 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) { } if (auto *tld = dyn_cast(dc)) { - if (dc->isAsyncContext() || - (dc->getASTContext().LangOpts.WarnConcurrency && - dc->getASTContext().LangOpts.EnableExperimentalAsyncTopLevel)) { + if (dc->isAsyncContext() || dc->getASTContext().LangOpts.WarnConcurrency) { if (Type mainActor = dc->getASTContext().getMainActorType()) return ActorIsolation::forGlobalActor(mainActor, /*unsafe=*/false); } diff --git a/lib/AST/DeclContext.cpp b/lib/AST/DeclContext.cpp index 5df2857586059..0715480eecd07 100644 --- a/lib/AST/DeclContext.cpp +++ b/lib/AST/DeclContext.cpp @@ -1239,12 +1239,10 @@ bool DeclContext::isAsyncContext() const { return false; case DeclContextKind::FileUnit: if (const SourceFile *sf = dyn_cast(this)) - return getASTContext().LangOpts.EnableExperimentalAsyncTopLevel && - sf->isAsyncTopLevelSourceFile(); + return sf->isAsyncTopLevelSourceFile(); return false; case DeclContextKind::TopLevelCodeDecl: - return getASTContext().LangOpts.EnableExperimentalAsyncTopLevel && - getParent()->isAsyncContext(); + return getParent()->isAsyncContext(); case DeclContextKind::AbstractClosureExpr: return cast(this)->isBodyAsync(); case DeclContextKind::AbstractFunctionDecl: { diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 31d0cb05dc316..9e9cddc9f2ae7 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -487,16 +487,15 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.DisableImplicitConcurrencyModuleImport |= Args.hasArg(OPT_disable_implicit_concurrency_module_import); - Opts.EnableExperimentalAsyncTopLevel |= - Args.hasArg(OPT_enable_experimental_async_top_level); - /// experimental distributed also implicitly enables experimental concurrency Opts.EnableExperimentalDistributed |= Args.hasArg(OPT_enable_experimental_distributed); Opts.EnableExperimentalConcurrency |= Args.hasArg(OPT_enable_experimental_distributed); - Opts.EnableExperimentalConcurrency |= - Args.hasArg(OPT_enable_experimental_async_top_level); + + if (Args.hasArg(OPT_enable_experimental_async_top_level)) + Diags.diagnose(SourceLoc(), diag::warn_flag_deprecated, + "-enable-experimental-async-top-level"); Opts.DiagnoseInvalidEphemeralnessAsError |= Args.hasArg(OPT_enable_invalid_ephemeralness_as_error); diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 4d85541791213..e0bff4cc03ae3 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -344,14 +344,14 @@ GlobalActorAttributeRequest::evaluate( if (auto var = dyn_cast(storage)) { // ... but not if it's an async-context top-level global - if (var->getASTContext().LangOpts.EnableExperimentalAsyncTopLevel && - var->isTopLevelGlobal() && (var->getDeclContext()->isAsyncContext() - || var->getASTContext().LangOpts.WarnConcurrency)) { + if (var->isTopLevelGlobal() && + (var->getDeclContext()->isAsyncContext() || + var->getASTContext().LangOpts.WarnConcurrency)) { var->diagnose(diag::global_actor_top_level_var) .highlight(globalActorAttr->getRangeWithAt()); return None; } - + // ... and not if it's local property if (var->getDeclContext()->isLocalContext()) { var->diagnose(diag::global_actor_on_local_variable, var->getName()) @@ -3869,8 +3869,7 @@ ActorIsolation ActorIsolationRequest::evaluate( } if (auto var = dyn_cast(value)) { - if (var->getASTContext().LangOpts.EnableExperimentalAsyncTopLevel && - var->isTopLevelGlobal() && + if (var->isTopLevelGlobal() && (var->getASTContext().LangOpts.WarnConcurrency || var->getDeclContext()->isAsyncContext())) { if (Type mainActor = var->getASTContext().getMainActorType()) From 8c13370242b80629d159ab8d1ee7823fdfd37cc5 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 10 Mar 2022 20:50:52 +0900 Subject: [PATCH 104/242] [Distributed] rename _Distributed to underscoreless module --- include/swift/AST/DiagnosticsSema.def | 2 +- include/swift/AST/KnownIdentifiers.def | 2 +- include/swift/AST/TypeCheckRequests.h | 2 +- include/swift/Option/FrontendOptions.td | 2 +- include/swift/Strings.h | 2 +- lib/AST/Decl.cpp | 2 +- lib/SILGen/SILGenDistributed.cpp | 2 +- lib/Sema/TypeCheckDistributed.cpp | 4 ++-- lib/Sema/TypeCheckDistributed.h | 2 +- stdlib/cmake/modules/AddSwiftStdlib.cmake | 2 +- stdlib/public/Distributed/CMakeLists.txt | 2 +- .../public/Distributed/DistributedMetadata.swift | 16 ++++++++-------- .../Inputs/BadDistributedActorSystems.swift | 2 +- .../Inputs/FakeDistributedActorSystems.swift | 2 +- .../Inputs/dynamic_replacement_da_decl.swift | 2 +- .../dynamic_replacement_da_extension.swift | 2 +- .../Runtime/distributed_actor_decode.swift | 2 +- .../Runtime/distributed_actor_deinit.swift | 2 +- ...ibuted_actor_func_calls_remoteCall_echo.swift | 2 +- ...buted_actor_func_calls_remoteCall_empty.swift | 2 +- ...actor_func_calls_remoteCall_genericFunc.swift | 2 +- ...buted_actor_func_calls_remoteCall_hello.swift | 2 +- ...ibuted_actor_func_calls_remoteCall_take.swift | 4 ++-- ...r_func_calls_remoteCall_takeThrowReturn.swift | 2 +- ...ed_actor_func_calls_remoteCall_take_two.swift | 4 ++-- ...buted_actor_func_calls_remoteCall_throw.swift | 2 +- .../Runtime/distributed_actor_init_local.swift | 2 +- .../Runtime/distributed_actor_isRemote.swift | 2 +- .../Runtime/distributed_actor_local.swift | 2 +- .../distributed_actor_remoteCall_roundtrip.swift | 4 ++-- ...uted_actor_remote_fieldsDontCrashDeinit.swift | 2 +- .../distributed_actor_remote_functions.swift | 4 ++-- ...distributed_actor_remote_retains_system.swift | 2 +- .../Runtime/distributed_actor_self_calls.swift | 2 +- .../Runtime/distributed_actor_whenLocal.swift | 2 +- .../Runtime/distributed_func_metadata.swift | 2 +- .../distributed_actor_default_deinit_sil.swift | 2 +- .../distributed_actor_default_init_sil_1.swift | 6 +++--- .../distributed_actor_default_init_sil_2.swift | 4 ++-- .../distributed_actor_default_init_sil_3.swift | 4 ++-- .../distributed_actor_default_init_sil_4.swift | 4 ++-- .../distributed_actor_default_init_sil_5.swift | 6 +++--- test/Distributed/actor_protocols.swift | 2 +- ...distributed_actor_accessor_section_coff.swift | 2 +- .../distributed_actor_accessor_section_elf.swift | 2 +- ...istributed_actor_accessor_section_macho.swift | 2 +- ...distributed_actor_accessor_thunks_32bit.swift | 2 +- ...distributed_actor_accessor_thunks_64bit.swift | 2 +- .../distributed_actor_async_let.swift | 2 +- test/Distributed/distributed_actor_basic.swift | 2 +- ...buted_actor_cannot_be_downcast_to_actor.swift | 2 +- .../distributed_actor_concurrency_warnings.swift | 4 ++-- ...uted_actor_func_implicitly_async_throws.swift | 2 +- test/Distributed/distributed_actor_generic.swift | 2 +- .../distributed_actor_inference.swift | 2 +- .../distributed_actor_initialization.swift | 2 +- ..._is_experimental_enabled_missing_import.swift | 8 ++++---- .../distributed_actor_isolation.swift | 2 +- .../distributed_actor_isolation_and_tasks.swift | 2 +- .../distributed_actor_nonisolated.swift | 2 +- .../distributed_actor_protocol_isolation.swift | 2 +- test/Distributed/distributed_actor_resolve.swift | 2 +- ..._system_missing_adhoc_requirement_impls.swift | 2 +- ...buted_actor_var_implicitly_async_throws.swift | 2 +- .../Distributed/distributed_missing_import.swift | 2 +- .../distributed_protocol_isolation.swift | 2 +- ...ributed_func_serialization_requirements.swift | 2 +- test/IRGen/distributed_actor.swift | 2 +- test/SILGen/distributed_thunk.swift | 2 +- test/ScanDependencies/can_import_with_map.swift | 2 +- .../explicit-framework-irgen.swift | 2 +- .../explicit-module-map-forwarding-module.swift | 2 +- test/Serialization/Inputs/def_distributed.swift | 2 +- test/Serialization/distributed.swift | 2 +- test/TBD/distributed.swift | 2 +- .../decl/protocol/special/DistributedActor.swift | 2 +- test/lit.cfg | 4 ++-- 77 files changed, 101 insertions(+), 101 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index d824ec647cdbb..854ef71461e69 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4534,7 +4534,7 @@ ERROR(distributed_actor_isolated_non_self_reference,none, "non-isolated context", (DescriptiveDeclKind, DeclName)) ERROR(distributed_actor_needs_explicit_distributed_import,none, - "'_Distributed' module not imported, required for 'distributed actor'", + "'Distributed' module not imported, required for 'distributed actor'", ()) ERROR(actor_isolated_inout_state,none, "actor-isolated %0 %1 cannot be passed 'inout' to" diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index b95957000c19a..fad9c795759ff 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -60,7 +60,7 @@ IDENTIFIER(CoreFoundation) IDENTIFIER(count) IDENTIFIER(CVarArg) IDENTIFIER(Darwin) -IDENTIFIER_(Distributed) +IDENTIFIER(Distributed) IDENTIFIER(dealloc) IDENTIFIER(debugDescription) IDENTIFIER(Decodable) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index eac22091a3006..239ca06f00c9a 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -2738,7 +2738,7 @@ class HasCircularRawValueRequest bool isCached() const { return true; } }; -/// Checks if the _Distributed module is available. +/// Checks if the Distributed module is available. class DistributedModuleIsAvailableRequest : public SimpleRequest { diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 86d99764ccc18..778f562cff7c8 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -408,7 +408,7 @@ def disable_implicit_concurrency_module_import : Flag<["-"], def disable_implicit_distributed_module_import : Flag<["-"], "disable-implicit-distributed-module-import">, - HelpText<"Disable the implicit import of the _Distributed module.">; + HelpText<"Disable the implicit import of the Distributed module.">; def disable_arc_opts : Flag<["-"], "disable-arc-opts">, HelpText<"Don't run SIL ARC optimization passes.">; diff --git a/include/swift/Strings.h b/include/swift/Strings.h index 23c7ccfae100e..0ae8fbd8d527d 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -25,7 +25,7 @@ constexpr static const StringLiteral SWIFT_ONONE_SUPPORT = "SwiftOnoneSupport"; /// The name of the Concurrency module, which supports that extension. constexpr static const StringLiteral SWIFT_CONCURRENCY_NAME = "_Concurrency"; /// The name of the Distributed module, which supports that extension. -constexpr static const StringLiteral SWIFT_DISTRIBUTED_NAME = "_Distributed"; +constexpr static const StringLiteral SWIFT_DISTRIBUTED_NAME = "Distributed"; /// The name of the StringProcessing module, which supports that extension. constexpr static const StringLiteral SWIFT_STRING_PROCESSING_NAME = "_StringProcessing"; /// The name of the SwiftShims module, which contains private stdlib decls. diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 576aaff8986d2..86da5b736924e 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -5569,7 +5569,7 @@ void ProtocolDecl::computeKnownProtocolKind() const { !module->getName().is("Foundation") && !module->getName().is("_Differentiation") && !module->getName().is("_Concurrency") && - !module->getName().is("_Distributed")) { + !module->getName().is("Distributed")) { const_cast(this)->Bits.ProtocolDecl.KnownProtocol = 1; return; } diff --git a/lib/SILGen/SILGenDistributed.cpp b/lib/SILGen/SILGenDistributed.cpp index 0acf739f552ee..4e3df16c62591 100644 --- a/lib/SILGen/SILGenDistributed.cpp +++ b/lib/SILGen/SILGenDistributed.cpp @@ -115,7 +115,7 @@ static void emitDistributedIfRemoteBranch(SILGenFunction &SGF, SILLocation Loc, FuncDecl *isRemoteFn = ctx.getIsRemoteDistributedActor(); assert(isRemoteFn && "Could not find 'is remote' function, is the " - "'_Distributed' module available?"); + "'Distributed' module available?"); ManagedValue selfAnyObject = B.createInitExistentialRef( Loc, SGF.getLoweredType(ctx.getAnyObjectType()), CanType(selfTy), diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp index 35c476ed39344..9fa49fbde9f88 100644 --- a/lib/Sema/TypeCheckDistributed.cpp +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -47,7 +47,7 @@ DistributedModuleIsAvailableRequest::evaluate(Evaluator &evaluator, if (C.getLoadedModule(C.Id_Distributed)) return true; - // seems we're missing the _Distributed module, ask to import it explicitly + // seems we're missing the Distributed module, ask to import it explicitly decl->diagnose(diag::distributed_actor_needs_explicit_distributed_import); return false; } @@ -639,7 +639,7 @@ void TypeChecker::checkDistributedActor(SourceFile *SF, NominalTypeDecl *nominal if (!nominal) return; - // ==== Ensure the _Distributed module is available, + // ==== Ensure the Distributed module is available, // without it there's no reason to check the decl in more detail anyway. if (!swift::ensureDistributedModuleLoaded(nominal)) return; diff --git a/lib/Sema/TypeCheckDistributed.h b/lib/Sema/TypeCheckDistributed.h index 6c0a3c7ec09f0..5765473ba2b51 100644 --- a/lib/Sema/TypeCheckDistributed.h +++ b/lib/Sema/TypeCheckDistributed.h @@ -34,7 +34,7 @@ class NominalTypeDecl; /********************* Distributed Actor Type Checking ************************/ /******************************************************************************/ -// Diagnose an error if the _Distributed module is not loaded. +// Diagnose an error if the Distributed module is not loaded. bool ensureDistributedModuleLoaded(Decl *decl); /// Check for illegal property declarations (e.g. re-declaring transport or id) diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index 2cca338cb5d53..6bbe58bcea298 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -1747,7 +1747,7 @@ function(add_swift_target_library name) # Turn off implicit import of _Concurrency when building libraries list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS "-Xfrontend;-disable-implicit-concurrency-module-import") - # Turn off implicit import of _Distributed when building libraries + # Turn off implicit import of Distributed when building libraries if(SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED) list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS "-Xfrontend;-disable-implicit-distributed-module-import") diff --git a/stdlib/public/Distributed/CMakeLists.txt b/stdlib/public/Distributed/CMakeLists.txt index 96bb0ab3fd478..46a171fd39007 100644 --- a/stdlib/public/Distributed/CMakeLists.txt +++ b/stdlib/public/Distributed/CMakeLists.txt @@ -14,7 +14,7 @@ set(swift_distributed_link_libraries swiftCore) -add_swift_target_library(swift_Distributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB +add_swift_target_library(swiftDistributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB DistributedActor.cpp DistributedActor.swift DistributedActorSystem.swift diff --git a/stdlib/public/Distributed/DistributedMetadata.swift b/stdlib/public/Distributed/DistributedMetadata.swift index 5a01a40a4110b..aee38fc93661c 100644 --- a/stdlib/public/Distributed/DistributedMetadata.swift +++ b/stdlib/public/Distributed/DistributedMetadata.swift @@ -16,7 +16,7 @@ import Swift /// /// - Returns: May return a negative number to signal a decoding error. @available(SwiftStdlib 5.7, *) -public // SPI _Distributed +public // SPI Distributed func _getParameterCount(mangledMethodName name: String) -> Int32 { let nameUTF8 = Array(name.utf8) return nameUTF8.withUnsafeBufferPointer { nameUTF8 in @@ -27,7 +27,7 @@ func _getParameterCount(mangledMethodName name: String) -> Int32 { @available(SwiftStdlib 5.7, *) @_silgen_name("swift_func_getParameterCount") -public // SPI _Distributed +public // SPI Distributed func __getParameterCount( _ typeNameStart: UnsafePointer, _ typeNameLength: UInt @@ -39,7 +39,7 @@ func __getParameterCount( /// - Returns: the actual number of types written, /// or negative value to signify an error @available(SwiftStdlib 5.7, *) -public // SPI _Distributed +public // SPI Distributed func _getParameterTypeInfo( mangledMethodName name: String, genericEnv: UnsafeRawPointer?, // GenericEnvironmentDescriptor * @@ -58,7 +58,7 @@ func _getParameterTypeInfo( /// or a negative value to signal decoding error. @available(SwiftStdlib 5.7, *) @_silgen_name("swift_func_getParameterTypeInfo") -public // SPI _Distributed +public // SPI Distributed func __getParameterTypeInfo( _ typeNameStart: UnsafePointer, _ typeNameLength: UInt, _ genericEnv: UnsafeRawPointer?, // GenericEnvironmentDescriptor * @@ -67,7 +67,7 @@ func __getParameterTypeInfo( ) -> Int32 @available(SwiftStdlib 5.7, *) -public // SPI _Distributed +public // SPI Distributed func _getReturnTypeInfo( mangledMethodName name: String, genericEnv: UnsafeRawPointer?, // GenericEnvironmentDescriptor * @@ -82,7 +82,7 @@ func _getReturnTypeInfo( @available(SwiftStdlib 5.7, *) @_silgen_name("swift_func_getReturnTypeInfo") -public // SPI _Distributed +public // SPI Distributed func __getReturnTypeInfo( _ typeNameStart: UnsafePointer, _ typeNameLength: UInt, @@ -95,7 +95,7 @@ func __getReturnTypeInfo( /// the given distributed target @available(SwiftStdlib 5.7, *) @_silgen_name("swift_distributed_getGenericEnvironment") -public // SPI _Distributed +public // SPI Distributed func _getGenericEnvironmentOfDistributedTarget( _ targetNameStart: UnsafePointer, _ targetNameLength: UInt @@ -103,7 +103,7 @@ func _getGenericEnvironmentOfDistributedTarget( @available(SwiftStdlib 5.7, *) @_silgen_name("swift_distributed_getWitnessTables") -public // SPI _Distributed +public // SPI Distributed func _getWitnessTablesFor( environment: UnsafeRawPointer, genericArguments: UnsafeRawPointer diff --git a/test/Distributed/Inputs/BadDistributedActorSystems.swift b/test/Distributed/Inputs/BadDistributedActorSystems.swift index 26ba103035ea7..8303860f568f9 100644 --- a/test/Distributed/Inputs/BadDistributedActorSystems.swift +++ b/test/Distributed/Inputs/BadDistributedActorSystems.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import _Distributed +import Distributed // ==== Fake Address ----------------------------------------------------------- diff --git a/test/Distributed/Inputs/FakeDistributedActorSystems.swift b/test/Distributed/Inputs/FakeDistributedActorSystems.swift index b7f276b903cef..d9f8611f6e5ad 100644 --- a/test/Distributed/Inputs/FakeDistributedActorSystems.swift +++ b/test/Distributed/Inputs/FakeDistributedActorSystems.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import _Distributed +import Distributed // ==== Fake Address ----------------------------------------------------------- diff --git a/test/Distributed/Inputs/dynamic_replacement_da_decl.swift b/test/Distributed/Inputs/dynamic_replacement_da_decl.swift index 17e237e57a7a2..564db85955074 100644 --- a/test/Distributed/Inputs/dynamic_replacement_da_decl.swift +++ b/test/Distributed/Inputs/dynamic_replacement_da_decl.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import _Distributed +import Distributed // ==== Fake Transport --------------------------------------------------------- diff --git a/test/Distributed/Inputs/dynamic_replacement_da_extension.swift b/test/Distributed/Inputs/dynamic_replacement_da_extension.swift index 160b4fefffd7d..0f8db405baf3b 100644 --- a/test/Distributed/Inputs/dynamic_replacement_da_extension.swift +++ b/test/Distributed/Inputs/dynamic_replacement_da_extension.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -import _Distributed +import Distributed extension DA { @_dynamicReplacement(for:_remote_hello(other:)) diff --git a/test/Distributed/Runtime/distributed_actor_decode.swift b/test/Distributed/Runtime/distributed_actor_decode.swift index 6d64e54ae656a..8f602a35c0d53 100644 --- a/test/Distributed/Runtime/distributed_actor_decode.swift +++ b/test/Distributed/Runtime/distributed_actor_decode.swift @@ -7,7 +7,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed distributed actor DA: CustomStringConvertible { typealias ActorSystem = FakeActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_deinit.swift b/test/Distributed/Runtime/distributed_actor_deinit.swift index 2abc117002ba5..f32524efd361a 100644 --- a/test/Distributed/Runtime/distributed_actor_deinit.swift +++ b/test/Distributed/Runtime/distributed_actor_deinit.swift @@ -10,7 +10,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed actor A {} diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_echo.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_echo.swift index 6583b545faadd..9573e781a81c2 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_echo.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_echo.swift @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_empty.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_empty.swift index 9d37fdab58e38..fa2e34d330f72 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_empty.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_empty.swift @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift index db62c1a0c35b3..5c9922d8ac32d 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_hello.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_hello.swift index 623161522b23a..197b3318c7d9e 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_hello.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_hello.swift @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take.swift index 092d207a74b22..efdcd0408a210 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift -// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out +// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s --color // REQUIRES: executable_test @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_takeThrowReturn.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_takeThrowReturn.swift index 222f6d40d378a..978707ebcb166 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_takeThrowReturn.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_takeThrowReturn.swift @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift index 97179d10be875..ae1310ed60d10 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift -// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out +// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s --color // REQUIRES: executable_test @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_throw.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_throw.swift index 6f110d876cdba..6fb76dbe2a450 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_throw.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_throw.swift @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_init_local.swift b/test/Distributed/Runtime/distributed_actor_init_local.swift index 3ade00856f643..2dcd74a111b53 100644 --- a/test/Distributed/Runtime/distributed_actor_init_local.swift +++ b/test/Distributed/Runtime/distributed_actor_init_local.swift @@ -10,7 +10,7 @@ // REQUIRES: radar_86543336 -import _Distributed +import Distributed enum MyError: Error { case test diff --git a/test/Distributed/Runtime/distributed_actor_isRemote.swift b/test/Distributed/Runtime/distributed_actor_isRemote.swift index 594bbe96c3c11..b511821085fca 100644 --- a/test/Distributed/Runtime/distributed_actor_isRemote.swift +++ b/test/Distributed/Runtime/distributed_actor_isRemote.swift @@ -11,7 +11,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed import FakeDistributedActorSystems distributed actor SomeSpecificDistributedActor { diff --git a/test/Distributed/Runtime/distributed_actor_local.swift b/test/Distributed/Runtime/distributed_actor_local.swift index eb06bc29bcaf3..8055101863bbd 100644 --- a/test/Distributed/Runtime/distributed_actor_local.swift +++ b/test/Distributed/Runtime/distributed_actor_local.swift @@ -11,7 +11,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed import FakeDistributedActorSystems distributed actor SomeSpecificDistributedActor { diff --git a/test/Distributed/Runtime/distributed_actor_remoteCall_roundtrip.swift b/test/Distributed/Runtime/distributed_actor_remoteCall_roundtrip.swift index 314c5e4475f22..fffb546d6e058 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCall_roundtrip.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCall_roundtrip.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend-emit-module -emit-module-path %t/FakeDistributedActorSystems.swiftmodule -module-name FakeDistributedActorSystems -disable-availability-checking %S/../Inputs/FakeDistributedActorSystems.swift -// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out +// RUN: %target-build-swift -module-name main -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s %S/../Inputs/FakeDistributedActorSystems.swift -o %t/a.out // RUN: %target-run %t/a.out | %FileCheck %s --enable-var-scope --color // X: %target-run-simple-swift( -Xfrontend -module-name=main -Xfrontend -disable-availability-checking -parse-as-library -Xfrontend -I -Xfrontend %t ) | %FileCheck %s @@ -16,7 +16,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_remote_fieldsDontCrashDeinit.swift b/test/Distributed/Runtime/distributed_actor_remote_fieldsDontCrashDeinit.swift index f79320d09173f..06c8936200b0e 100644 --- a/test/Distributed/Runtime/distributed_actor_remote_fieldsDontCrashDeinit.swift +++ b/test/Distributed/Runtime/distributed_actor_remote_fieldsDontCrashDeinit.swift @@ -11,7 +11,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed distributed actor SomeSpecificDistributedActor { typealias Transport = FakeActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_remote_functions.swift b/test/Distributed/Runtime/distributed_actor_remote_functions.swift index 3f60926193053..a12103c470bb6 100644 --- a/test/Distributed/Runtime/distributed_actor_remote_functions.swift +++ b/test/Distributed/Runtime/distributed_actor_remote_functions.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency @@ -8,7 +8,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed import _Concurrency struct Boom: Error { diff --git a/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift b/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift index 7635a133cf01a..831a9f0b47545 100644 --- a/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift +++ b/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift @@ -8,7 +8,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed distributed actor SomeSpecificDistributedActor { deinit { diff --git a/test/Distributed/Runtime/distributed_actor_self_calls.swift b/test/Distributed/Runtime/distributed_actor_self_calls.swift index 105c58debccd7..2afcc63c9a1c1 100644 --- a/test/Distributed/Runtime/distributed_actor_self_calls.swift +++ b/test/Distributed/Runtime/distributed_actor_self_calls.swift @@ -9,7 +9,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed distributed actor Philosopher { diff --git a/test/Distributed/Runtime/distributed_actor_whenLocal.swift b/test/Distributed/Runtime/distributed_actor_whenLocal.swift index b338a8dfeff22..c9bf082208bec 100644 --- a/test/Distributed/Runtime/distributed_actor_whenLocal.swift +++ b/test/Distributed/Runtime/distributed_actor_whenLocal.swift @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed distributed actor Capybara { // only the local capybara can do this! diff --git a/test/Distributed/Runtime/distributed_func_metadata.swift b/test/Distributed/Runtime/distributed_func_metadata.swift index bc3de6e9ac7e3..c675031b8a806 100644 --- a/test/Distributed/Runtime/distributed_func_metadata.swift +++ b/test/Distributed/Runtime/distributed_func_metadata.swift @@ -11,7 +11,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed struct SomeValue: Sendable, Codable {} diff --git a/test/Distributed/SIL/distributed_actor_default_deinit_sil.swift b/test/Distributed/SIL/distributed_actor_default_deinit_sil.swift index 1f4ec99d91c99..8ed9cd9636c43 100644 --- a/test/Distributed/SIL/distributed_actor_default_deinit_sil.swift +++ b/test/Distributed/SIL/distributed_actor_default_deinit_sil.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_1.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_1.swift index 88848b915fa2c..246d1bc0997b1 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_1.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_1.swift @@ -6,7 +6,7 @@ /// The convention in this test is that the Swift declaration comes before its FileCheck lines. -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem @@ -35,7 +35,7 @@ distributed actor MyDistActor { // CHECK: store [[SYSTEM]] to [[TP_FIELD]] : $*FakeActorSystem // *** obtain an identity *** // CHECK: [[SELF_METATYPE:%[0-9]+]] = metatype $@thick MyDistActor.Type -// CHECK: [[ASSIGN_ID_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV8assignIDyAA0C7AddressVxm01_B00bC0RzAF0G0RtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@thick τ_0_0.Type, @guaranteed FakeActorSystem) -> @owned ActorAddress +// CHECK: [[ASSIGN_ID_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV8assignIDyAA0C7AddressVxm0B00bC0RzAF0G0RtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@thick τ_0_0.Type, @guaranteed FakeActorSystem) -> @owned ActorAddress // user: %12 // CHECK: [[ID:%[0-9]+]] = apply [[ASSIGN_ID_FN]]([[SELF_METATYPE]], [[SYSTEM]]) : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@thick τ_0_0.Type, @guaranteed FakeActorSystem) -> @owned ActorAddress // *** save identity *** // CHECK: [[ID_FIELD:%[0-9]+]] = ref_element_addr [[SELF]] : $MyDistActor, #MyDistActor.id @@ -43,7 +43,7 @@ distributed actor MyDistActor { // *** save user-defined property *** // CHECK: store {{%[0-9]+}} to {{%[0-9]+}} : $*SomeClass // *** invoke actorReady *** -// CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx01_B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () +// CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () // CHECK: = apply [[READY_FN]]([[SELF]], [[SYSTEM]]) // *** clean-ups *** // CHECK: release_value [[SYSTEM]] : $FakeActorSystem diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_2.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_2.swift index a1d0589f26052..8235e3cb91f52 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_2.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_2.swift @@ -6,7 +6,7 @@ /// The convention in this test is that the Swift declaration comes before its FileCheck lines. -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem @@ -40,7 +40,7 @@ distributed actor MyDistActor { // CHECK: cond_br [[RAW_BOOL]], [[SUCCESS_BB:bb[0-9]+]], [[FAIL_BB:bb[0-9]+]] // CHECK: [[SUCCESS_BB]]: -// CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx01_B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () +// CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () // CHECK: = apply [[READY_FN]] // CHECK: br [[RET_BB:bb[0-9]+]] diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_3.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_3.swift index 88e892ebfc072..4e5d529b028e7 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_3.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_3.swift @@ -6,7 +6,7 @@ /// The convention in this test is that the Swift declaration comes before its FileCheck lines. -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem @@ -34,7 +34,7 @@ distributed actor MyDistActor { // CHECK: [[SUCCESS_BB]]: // CHECK: hop_to_executor {{%[0-9]+}} - // CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx01_B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () + // CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () // CHECK: = apply [[READY_FN]] // CHECK: br [[RET_BB:bb[0-9]+]] diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_4.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_4.swift index 2c116b90d4109..4db4ea601e17f 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_4.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_4.swift @@ -6,7 +6,7 @@ /// The convention in this test is that the Swift declaration comes before its FileCheck lines. -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem @@ -34,7 +34,7 @@ distributed actor MyDistActor { // CHECK: [[SUCCESS_BB]]: // CHECK: hop_to_executor {{%[0-9]+}} - // CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx01_B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () + // CHECK: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () // CHECK: = apply [[READY_FN]] // CHECK: br [[RET_BB:bb[0-9]+]] diff --git a/test/Distributed/SIL/distributed_actor_default_init_sil_5.swift b/test/Distributed/SIL/distributed_actor_default_init_sil_5.swift index 3ae2d6cc2aa00..53fbd8067d816 100644 --- a/test/Distributed/SIL/distributed_actor_default_init_sil_5.swift +++ b/test/Distributed/SIL/distributed_actor_default_init_sil_5.swift @@ -7,7 +7,7 @@ /// The convention in this test is that the Swift declaration comes before its FileCheck lines. -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem @@ -45,7 +45,7 @@ distributed actor MyDistActor { // CHECK-NEXT: retain_value [[SYSTEM]] : $FakeActorSystem // CHECK-NEXT: retain_value [[SYSTEM]] : $FakeActorSystem // CHECK-NEXT: // function_ref FakeActorSystem.actorReady(_:) -// CHECK-NEXT: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx01_B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () +// CHECK-NEXT: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () // CHECK-NEXT: [[APPLIED:%[0-9]+]] = apply [[READY_FN]] // CHECK: br [[JOIN:bb[0-9]+]] @@ -68,7 +68,7 @@ distributed actor MyDistActor { // CHECK-NEXT: retain_value [[SYSTEM]] : $FakeActorSystem // CHECK-NEXT: retain_value [[SYSTEM]] : $FakeActorSystem // CHECK-NEXT: // function_ref FakeActorSystem.actorReady(_:) -// CHECK-NEXT: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx01_B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () +// CHECK-NEXT: [[READY_FN:%[0-9]+]] = function_ref @$s27FakeDistributedActorSystems0aC6SystemV10actorReadyyyx0B00bC0RzAA0C7AddressV2IDRtzlF : $@convention(method) <τ_0_0 where τ_0_0 : DistributedActor, τ_0_0.ID == ActorAddress> (@guaranteed τ_0_0, @guaranteed FakeActorSystem) -> () // CHECK-NEXT: = apply [[READY_FN]] // CHECK: return // CHECK: } // end sil function '$s14default_deinit11MyDistActorC12system_async4condAC015FakeDistributedE7Systems0iE6SystemV_SbtYacfc' diff --git a/test/Distributed/actor_protocols.swift b/test/Distributed/actor_protocols.swift index 87fe93ced6af5..9c0699b14768c 100644 --- a/test/Distributed/actor_protocols.swift +++ b/test/Distributed/actor_protocols.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Distributed/distributed_actor_accessor_section_coff.swift b/test/Distributed/distributed_actor_accessor_section_coff.swift index 50ccf41c7c253..bd2a5d032ce7d 100644 --- a/test/Distributed/distributed_actor_accessor_section_coff.swift +++ b/test/Distributed/distributed_actor_accessor_section_coff.swift @@ -10,7 +10,7 @@ // FIXME: Test is temporary disabled (no way to debug) // REQUIRES: fix -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_actor_accessor_section_elf.swift b/test/Distributed/distributed_actor_accessor_section_elf.swift index 7c255f5a17a9a..9ccf828abdaae 100644 --- a/test/Distributed/distributed_actor_accessor_section_elf.swift +++ b/test/Distributed/distributed_actor_accessor_section_elf.swift @@ -8,7 +8,7 @@ // REQUIRES: OS=linux-gnu -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_actor_accessor_section_macho.swift b/test/Distributed/distributed_actor_accessor_section_macho.swift index 24a9ebd7eb60b..3974f811ec16f 100644 --- a/test/Distributed/distributed_actor_accessor_section_macho.swift +++ b/test/Distributed/distributed_actor_accessor_section_macho.swift @@ -8,7 +8,7 @@ // REQUIRES: VENDOR=apple -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.7, *) diff --git a/test/Distributed/distributed_actor_accessor_thunks_32bit.swift b/test/Distributed/distributed_actor_accessor_thunks_32bit.swift index 52467f910c463..b5c353f68670c 100644 --- a/test/Distributed/distributed_actor_accessor_thunks_32bit.swift +++ b/test/Distributed/distributed_actor_accessor_thunks_32bit.swift @@ -10,7 +10,7 @@ // UNSUPPORTED: OS=windows-msvc -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_actor_accessor_thunks_64bit.swift b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift index cc15ad6ec491e..0902003307832 100644 --- a/test/Distributed/distributed_actor_accessor_thunks_64bit.swift +++ b/test/Distributed/distributed_actor_accessor_thunks_64bit.swift @@ -10,7 +10,7 @@ // UNSUPPORTED: OS=windows-msvc -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_actor_async_let.swift b/test/Distributed/distributed_actor_async_let.swift index 01c81e4e309b2..7eb6953895482 100644 --- a/test/Distributed/distributed_actor_async_let.swift +++ b/test/Distributed/distributed_actor_async_let.swift @@ -6,7 +6,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems distributed actor Philosopher { diff --git a/test/Distributed/distributed_actor_basic.swift b/test/Distributed/distributed_actor_basic.swift index f257b11f290f0..42396f43dbb2d 100644 --- a/test/Distributed/distributed_actor_basic.swift +++ b/test/Distributed/distributed_actor_basic.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Distributed/distributed_actor_cannot_be_downcast_to_actor.swift b/test/Distributed/distributed_actor_cannot_be_downcast_to_actor.swift index 4533497eb4531..4552473283930 100644 --- a/test/Distributed/distributed_actor_cannot_be_downcast_to_actor.swift +++ b/test/Distributed/distributed_actor_cannot_be_downcast_to_actor.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Distributed/distributed_actor_concurrency_warnings.swift b/test/Distributed/distributed_actor_concurrency_warnings.swift index 2205cd939d9cc..d275a8229c1d8 100644 --- a/test/Distributed/distributed_actor_concurrency_warnings.swift +++ b/test/Distributed/distributed_actor_concurrency_warnings.swift @@ -2,11 +2,11 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed actor Charlie { // should not cause sendable warnings, Worker is Sendable as implied by DA func two() -> Set where Worker: DistributedActor { [] } -} \ No newline at end of file +} diff --git a/test/Distributed/distributed_actor_func_implicitly_async_throws.swift b/test/Distributed/distributed_actor_func_implicitly_async_throws.swift index c66a690420ee1..92f6baac5eaf0 100644 --- a/test/Distributed/distributed_actor_func_implicitly_async_throws.swift +++ b/test/Distributed/distributed_actor_func_implicitly_async_throws.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_actor_generic.swift b/test/Distributed/distributed_actor_generic.swift index 72e8272ba23d7..68edaac7cf137 100644 --- a/test/Distributed/distributed_actor_generic.swift +++ b/test/Distributed/distributed_actor_generic.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Distributed/distributed_actor_inference.swift b/test/Distributed/distributed_actor_inference.swift index bca4e51b3ffc1..d801e6bc19446 100644 --- a/test/Distributed/distributed_actor_inference.swift +++ b/test/Distributed/distributed_actor_inference.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Distributed/distributed_actor_initialization.swift b/test/Distributed/distributed_actor_initialization.swift index 81880626d9a9d..82ad5a3b13a5f 100644 --- a/test/Distributed/distributed_actor_initialization.swift +++ b/test/Distributed/distributed_actor_initialization.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_actor_is_experimental_enabled_missing_import.swift b/test/Distributed/distributed_actor_is_experimental_enabled_missing_import.swift index 2b740a338a2ef..3e409c96e53be 100644 --- a/test/Distributed/distributed_actor_is_experimental_enabled_missing_import.swift +++ b/test/Distributed/distributed_actor_is_experimental_enabled_missing_import.swift @@ -5,7 +5,7 @@ actor SomeActor {} distributed actor DA {} -// expected-error@-1{{'_Distributed' module not imported, required for 'distributed actor'}} +// expected-error@-1{{'Distributed' module not imported, required for 'distributed actor'}} distributed actor class DAC {} // expected-error@-1{{distributed' can only be applied to 'actor' definitions, and distributed actor-isolated async functions}} @@ -18,18 +18,18 @@ actor A { } actor B { - distributed var neverOk: String { // expected-error{{'_Distributed' module not imported, required for 'distributed actor'}} + distributed var neverOk: String { // expected-error{{'Distributed' module not imported, required for 'distributed actor'}} "" } } distributed actor DA2 { - // expected-error@-1{{'_Distributed' module not imported, required for 'distributed actor'}} + // expected-error@-1{{'Distributed' module not imported, required for 'distributed actor'}} func normal() async {} distributed func dist() {} distributed func distAsync() async {} - distributed var neverOk: String { // expected-error{{'_Distributed' module not imported, required for 'distributed actor'}} + distributed var neverOk: String { // expected-error{{'Distributed' module not imported, required for 'distributed actor'}} "" } } diff --git a/test/Distributed/distributed_actor_isolation.swift b/test/Distributed/distributed_actor_isolation.swift index da06324d64f31..50a57f9f8c42d 100644 --- a/test/Distributed/distributed_actor_isolation.swift +++ b/test/Distributed/distributed_actor_isolation.swift @@ -7,7 +7,7 @@ // TODO(distributed): rdar://82419661 remove -verify-ignore-unknown here, no warnings should be emitted for our // generated code but right now a few are, because of Sendability checks -- need to track it down more. -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_actor_isolation_and_tasks.swift b/test/Distributed/distributed_actor_isolation_and_tasks.swift index e90259b7ab3c7..56683c53c4f34 100644 --- a/test/Distributed/distributed_actor_isolation_and_tasks.swift +++ b/test/Distributed/distributed_actor_isolation_and_tasks.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_actor_nonisolated.swift b/test/Distributed/distributed_actor_nonisolated.swift index 55fea7c44cabe..f4f666d762e4c 100644 --- a/test/Distributed/distributed_actor_nonisolated.swift +++ b/test/Distributed/distributed_actor_nonisolated.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_actor_protocol_isolation.swift b/test/Distributed/distributed_actor_protocol_isolation.swift index d1a3a3361d689..21ed662500d85 100644 --- a/test/Distributed/distributed_actor_protocol_isolation.swift +++ b/test/Distributed/distributed_actor_protocol_isolation.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Distributed/distributed_actor_resolve.swift b/test/Distributed/distributed_actor_resolve.swift index 3dafb3b749ff8..c85000078ffa4 100644 --- a/test/Distributed/distributed_actor_resolve.swift +++ b/test/Distributed/distributed_actor_resolve.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift b/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift index be10bc5e2799c..f0f89a1f3bc40 100644 --- a/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift +++ b/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed struct MissingRemoteCall: DistributedActorSystem { // expected-error@-1{{struct 'MissingRemoteCall' is missing witness for protocol requirement 'remoteCall'}} diff --git a/test/Distributed/distributed_actor_var_implicitly_async_throws.swift b/test/Distributed/distributed_actor_var_implicitly_async_throws.swift index f7f2c8847d259..9d8c4b83e89bc 100644 --- a/test/Distributed/distributed_actor_var_implicitly_async_throws.swift +++ b/test/Distributed/distributed_actor_var_implicitly_async_throws.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems @available(SwiftStdlib 5.5, *) diff --git a/test/Distributed/distributed_missing_import.swift b/test/Distributed/distributed_missing_import.swift index ecf3d34f5236b..db0c781a130f4 100644 --- a/test/Distributed/distributed_missing_import.swift +++ b/test/Distributed/distributed_missing_import.swift @@ -5,7 +5,7 @@ actor SomeActor { } distributed actor MissingImportDistributedActor_0 { } -// expected-error@-1{{'_Distributed' module not imported, required for 'distributed actor'}} +// expected-error@-1{{'Distributed' module not imported, required for 'distributed actor'}} let t: DistributedActorSystem // expected-error{{cannot find type 'DistributedActorSystem' in scope}} let a: ActorAddress // expected-error{{cannot find type 'ActorAddress' in scope}} diff --git a/test/Distributed/distributed_protocol_isolation.swift b/test/Distributed/distributed_protocol_isolation.swift index c20effd3a916d..0cec5e75be001 100644 --- a/test/Distributed/distributed_protocol_isolation.swift +++ b/test/Distributed/distributed_protocol_isolation.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems /// Use the existential wrapper as the default actor system. diff --git a/test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift b/test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift index 5df962472d68a..333970fb531a2 100644 --- a/test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift +++ b/test/Distributed/distributed_protocols_distributed_func_serialization_requirements.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems struct NotCodable {} diff --git a/test/IRGen/distributed_actor.swift b/test/IRGen/distributed_actor.swift index 6a9f54741cd0e..650b9c27b6fe3 100644 --- a/test/IRGen/distributed_actor.swift +++ b/test/IRGen/distributed_actor.swift @@ -3,7 +3,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed // Type descriptor. // CHECK-LABEL: @"$s17distributed_actor7MyActorC2idAA0D7AddressVvpWvd" diff --git a/test/SILGen/distributed_thunk.swift b/test/SILGen/distributed_thunk.swift index 706fc243e22e7..8a7cef11da516 100644 --- a/test/SILGen/distributed_thunk.swift +++ b/test/SILGen/distributed_thunk.swift @@ -2,7 +2,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed distributed actor DA { typealias ActorSystem = FakeActorSystem diff --git a/test/ScanDependencies/can_import_with_map.swift b/test/ScanDependencies/can_import_with_map.swift index 3df7d64b2a632..5264d967ccc36 100644 --- a/test/ScanDependencies/can_import_with_map.swift +++ b/test/ScanDependencies/can_import_with_map.swift @@ -27,7 +27,7 @@ // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}," >> %/t/inputs/map.json // RUN: echo "{" >> %/t/inputs/map.json -// RUN: echo "\"moduleName\": \"_Distributed\"," >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"Distributed\"," >> %/t/inputs/map.json // RUN: echo "\"modulePath\": \"%/distributed_module\"," >> %/t/inputs/map.json // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json diff --git a/test/ScanDependencies/explicit-framework-irgen.swift b/test/ScanDependencies/explicit-framework-irgen.swift index a1f9bca25c3ef..001251b759978 100644 --- a/test/ScanDependencies/explicit-framework-irgen.swift +++ b/test/ScanDependencies/explicit-framework-irgen.swift @@ -28,7 +28,7 @@ // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}," >> %/t/inputs/map.json // RUN: echo "{" >> %/t/inputs/map.json -// RUN: echo "\"moduleName\": \"_Distributed\"," >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"Distributed\"," >> %/t/inputs/map.json // RUN: echo "\"modulePath\": \"%/distributed_module\"," >> %/t/inputs/map.json // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json diff --git a/test/ScanDependencies/explicit-module-map-forwarding-module.swift b/test/ScanDependencies/explicit-module-map-forwarding-module.swift index 26bafeabfd3fc..1a27d0ac4e951 100644 --- a/test/ScanDependencies/explicit-module-map-forwarding-module.swift +++ b/test/ScanDependencies/explicit-module-map-forwarding-module.swift @@ -37,7 +37,7 @@ // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}," >> %/t/inputs/map.json // RUN: echo "{" >> %/t/inputs/map.json -// RUN: echo "\"moduleName\": \"_Distributed\"," >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"Distributed\"," >> %/t/inputs/map.json // RUN: echo "\"modulePath\": \"%/distributed_module\"," >> %/t/inputs/map.json // RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json // RUN: echo "}]" >> %/t/inputs/map.json diff --git a/test/Serialization/Inputs/def_distributed.swift b/test/Serialization/Inputs/def_distributed.swift index bb8070433fc38..977d6962718e7 100644 --- a/test/Serialization/Inputs/def_distributed.swift +++ b/test/Serialization/Inputs/def_distributed.swift @@ -1,4 +1,4 @@ -import _Distributed +import Distributed /// Use the existential wrapper as the default actor system. typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Serialization/distributed.swift b/test/Serialization/distributed.swift index 175540141513f..d4afca54a52cc 100644 --- a/test/Serialization/distributed.swift +++ b/test/Serialization/distributed.swift @@ -7,7 +7,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import def_distributed func testDoSomethingDistributed(system: FakeActorSystem) { diff --git a/test/TBD/distributed.swift b/test/TBD/distributed.swift index cb775c72024f7..3de6fce702e1d 100644 --- a/test/TBD/distributed.swift +++ b/test/TBD/distributed.swift @@ -6,7 +6,7 @@ // RUN: %target-swift-frontend %s -enable-testing -disable-availability-checking -emit-ir -o %t/test.ll -emit-tbd -emit-tbd-path %t/test.tbd -I %t // RUN cat %t/test.tbd | %FileCheck %s --dump-input=always -import _Distributed +import Distributed // CHECK: @"$s4test1AC13_remote_helloyyYaKFTE" = hidden global %swift.async_func_pointer // CHECK: @"$s4test1AC13_remote_helloyyYaKFTETu" = hidden global %swift.async_func_pointer diff --git a/test/decl/protocol/special/DistributedActor.swift b/test/decl/protocol/special/DistributedActor.swift index 5e99716933a02..239059bcbacec 100644 --- a/test/decl/protocol/special/DistributedActor.swift +++ b/test/decl/protocol/special/DistributedActor.swift @@ -2,7 +2,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed /// Use the existential wrapper as the default actor system. typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/lit.cfg b/test/lit.cfg index bf0db1420ea0a..e6a8bfea1c010 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -1772,8 +1772,8 @@ config.substitutions.append(('%concurrency_module', concurrency_module)) config.substitutions.append(('%/concurrency_module', '/'.join(os.path.normpath(concurrency_module).split(os.sep)))) -# Add 'distributed_module' as the path to the _Distributed .swiftmodule file -distributed_module = os.path.join(stdlib_dir, "_Distributed.swiftmodule", +# Add 'distributed_module' as the path to the Distributed .swiftmodule file +distributed_module = os.path.join(stdlib_dir, "Distributed.swiftmodule", target_specific_module_triple + ".swiftmodule") config.substitutions.append(('%distributed_module', distributed_module)) config.substitutions.append(('%/distributed_module', From 4f8c0152c3935a75c833b3b3f28675ea50432ed4 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 11 Mar 2022 12:53:37 +0900 Subject: [PATCH 105/242] update some missing places with _ --- stdlib/public/Distributed/CMakeLists.txt | 2 +- stdlib/public/Distributed/DistributedActor.cpp | 4 ++-- stdlib/public/SwiftShims/Visibility.h | 6 +++--- unittests/runtime/CMakeLists.txt | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stdlib/public/Distributed/CMakeLists.txt b/stdlib/public/Distributed/CMakeLists.txt index 46a171fd39007..2f02df695ddad 100644 --- a/stdlib/public/Distributed/CMakeLists.txt +++ b/stdlib/public/Distributed/CMakeLists.txt @@ -30,7 +30,7 @@ add_swift_target_library(swiftDistributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS LINK_LIBRARIES ${swift_distributed_link_libraries} C_COMPILE_FLAGS - -Dswift_Distributed_EXPORTS + -DswiftDistributed_EXPORTS -I${SWIFT_SOURCE_DIR}/stdlib/include SWIFT_COMPILE_FLAGS ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} diff --git a/stdlib/public/Distributed/DistributedActor.cpp b/stdlib/public/Distributed/DistributedActor.cpp index 604f41c00396b..c859a6a7652ab 100644 --- a/stdlib/public/Distributed/DistributedActor.cpp +++ b/stdlib/public/Distributed/DistributedActor.cpp @@ -33,7 +33,7 @@ findDistributedAccessor(const char *targetNameStart, size_t targetNameLength) { } SWIFT_CC(swift) -SWIFT_EXPORT_FROM(swift_Distributed) +SWIFT_EXPORT_FROM(swiftDistributed) void *swift_distributed_getGenericEnvironment(const char *targetNameStart, size_t targetNameLength) { auto *accessor = findDistributedAccessor(targetNameStart, targetNameLength); @@ -65,7 +65,7 @@ using TargetExecutorSignature = /*throws=*/true>; SWIFT_CC(swiftasync) -SWIFT_EXPORT_FROM(swift_Distributed) +SWIFT_EXPORT_FROM(swiftDistributed) TargetExecutorSignature::FunctionType swift_distributed_execute_target; /// Accessor takes: diff --git a/stdlib/public/SwiftShims/Visibility.h b/stdlib/public/SwiftShims/Visibility.h index 9fd8b0453b733..2144f01cdc169 100644 --- a/stdlib/public/SwiftShims/Visibility.h +++ b/stdlib/public/SwiftShims/Visibility.h @@ -187,10 +187,10 @@ #else #define SWIFT_IMAGE_EXPORTS_swift_Concurrency 0 #endif -#if defined(swift_Distributed_EXPORTS) -#define SWIFT_IMAGE_EXPORTS_swift_Distributed 1 +#if defined(swiftDistributed_EXPORTS) +#define SWIFT_IMAGE_EXPORTS_swiftDistributed 1 #else -#define SWIFT_IMAGE_EXPORTS_swift_Distributed 0 +#define SWIFT_IMAGE_EXPORTS_swiftDistributed 0 #endif #if defined(swift_Differentiation_EXPORTS) #define SWIFT_IMAGE_EXPORTS_swift_Differentiation 1 diff --git a/unittests/runtime/CMakeLists.txt b/unittests/runtime/CMakeLists.txt index 9795056699383..ab1c41840a9ac 100644 --- a/unittests/runtime/CMakeLists.txt +++ b/unittests/runtime/CMakeLists.txt @@ -93,7 +93,7 @@ if(("${SWIFT_HOST_VARIANT_SDK}" STREQUAL "${SWIFT_PRIMARY_VARIANT_SDK}") AND # DistributedActor.cpp # ) list(APPEND PLATFORM_TARGET_LINK_LIBRARIES - swift_Distributed${SWIFT_PRIMARY_VARIANT_SUFFIX} + swiftDistributed${SWIFT_PRIMARY_VARIANT_SUFFIX} ) endif() From 3321c324567e2f8cd74cf0463a5685aa44278a3a Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Wed, 9 Mar 2022 23:51:14 -0800 Subject: [PATCH 106/242] Pass all the tests! There were some tests that relied on the top-level code not being an asynchronous context to emit certain error messages. Now that it is, those tests weren't emitting the expected error message. In other cases, the issue was that they were trying to initialize a global variable and weren't really using top-level code as top-level code, so adding `-parse-as-library` was sufficient for the testing purposes. To fix the objc_async test, parsing as a library was nearly sufficient. Unfortunately, the little `if #available` trick that I was using stopped working since it relied on being in top-level code. So that we emit the unavailableFromAsync error message, I had to set the availability on everything correctly because we can't just disable availability checking. --- test/ClangImporter/objc_async.swift | 30 +++++++++++++++---- .../actor_call_implicitly_async.swift | 2 +- test/Concurrency/actor_isolation.swift | 2 +- test/Concurrency/concurrency_warnings.swift | 2 +- .../concurrent_value_checking.swift | 2 +- .../property_initializers_swift6.swift | 20 ++++++++----- test/stmt/async.swift | 13 +++++--- 7 files changed, 49 insertions(+), 22 deletions(-) diff --git a/test/ClangImporter/objc_async.swift b/test/ClangImporter/objc_async.swift index e0ed183f1e88a..a7dafdb3e2e49 100644 --- a/test/ClangImporter/objc_async.swift +++ b/test/ClangImporter/objc_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -verify -verify-additional-file %swift_src_root/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h -warn-concurrency +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules %s -verify -verify-additional-file %swift_src_root/test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h -warn-concurrency -parse-as-library // REQUIRES: objc_interop // REQUIRES: concurrency @@ -6,10 +6,10 @@ import Foundation import ObjCConcurrency // expected-remark@-1{{add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'ObjCConcurrency'}} -if #available(SwiftStdlib 5.5, *) { - +@available(SwiftStdlib 5.5, *) @MainActor func onlyOnMainActor() { } +@available(SwiftStdlib 5.5, *) func testSlowServer(slowServer: SlowServer) async throws { let _: Int = await slowServer.doSomethingSlow("mail") let _: Bool = await slowServer.checkAvailability() @@ -62,6 +62,7 @@ func testSlowServer(slowServer: SlowServer) async throws { _ = await slowServer.runOnMainThread() } +@available(SwiftStdlib 5.5, *) func testSlowServerSynchronous(slowServer: SlowServer) { // synchronous version let _: Int = slowServer.doSomethingConflicted("thinking") @@ -88,6 +89,7 @@ func testSlowServerSynchronous(slowServer: SlowServer) { let _: Int = slowServer.overridableButRunsOnMainThread // expected-error{{cannot convert value of type '(((String) -> Void)?) -> Void' to specified type 'Int'}} } +@available(SwiftStdlib 5.5, *) func testSlowServerOldSchool(slowServer: SlowServer) { slowServer.doSomethingSlow("mail") { i in _ = i @@ -96,6 +98,7 @@ func testSlowServerOldSchool(slowServer: SlowServer) { _ = slowServer.allOperations } +@available(SwiftStdlib 5.5, *) func testSendable(fn: () -> Void) { doSomethingConcurrently(fn) // okay, due to implicit @preconcurrency doSomethingConcurrentlyButUnsafe(fn) // okay, @Sendable not part of the type @@ -107,6 +110,7 @@ func testSendable(fn: () -> Void) { } } +@available(SwiftStdlib 5.5, *) func testSendableInAsync() async { var x = 17 doSomethingConcurrentlyButUnsafe { @@ -115,6 +119,7 @@ func testSendableInAsync() async { print(x) } +@available(SwiftStdlib 5.5, *) func testSendableAttrs( sendableClass: SendableClass, nonSendableClass: NonSendableClass, sendableEnum: SendableEnum, nonSendableEnum: NonSendableEnum, @@ -150,8 +155,10 @@ func testSendableAttrs( } // Check import of attributes +@available(SwiftStdlib 5.5, *) func globalAsync() async { } +@available(SwiftStdlib 5.5, *) actor MySubclassCheckingSwiftAttributes : ProtocolWithSwiftAttributes { func syncMethod() { } // expected-note 2{{calls to instance method 'syncMethod()' from outside of its actor context are implicitly asynchronous}} @@ -177,13 +184,16 @@ func testCV(r: NSRange) { // Global actor (unsafe) isolation. +@available(SwiftStdlib 5.5, *) actor SomeActor { } +@available(SwiftStdlib 5.5, *) @globalActor struct SomeGlobalActor { static let shared = SomeActor() } +@available(SwiftStdlib 5.5, *) class MyButton : NXButton { @MainActor func testMain() { onButtonPress() // okay @@ -198,17 +208,19 @@ class MyButton : NXButton { } } +@available(SwiftStdlib 5.5, *) func testButtons(mb: MyButton) { mb.onButtonPress() } - +@available(SwiftStdlib 5.5, *) func testMirrored(instance: ClassWithAsync) async { await instance.instanceAsync() await instance.protocolMethod() await instance.customAsyncName() } +@available(SwiftStdlib 5.5, *) @MainActor class MyToolbarButton : NXButton { var count = 5 @@ -220,6 +232,7 @@ func testMirrored(instance: ClassWithAsync) async { } } +@available(SwiftStdlib 5.5, *) @MainActor class MyView: NXView { func f() { Task { @@ -231,13 +244,17 @@ func testMirrored(instance: ClassWithAsync) async { } + +@available(SwiftStdlib 5.5, *) @MainActor func mainActorFn() {} +@available(SwiftStdlib 5.5, *) @SomeGlobalActor func sgActorFn() {} // Check inferred isolation for overridden decls from ObjC. // Note that even if the override is not present, it // can have an affect. -- rdar://87217618 / SR-15694 @MainActor +@available(SwiftStdlib 5.5, *) class FooFrame: PictureFrame { init() { super.init(size: 0) @@ -252,6 +269,7 @@ class FooFrame: PictureFrame { } } +@available(SwiftStdlib 5.5, *) class BarFrame: PictureFrame { init() { super.init(size: 0) @@ -266,6 +284,7 @@ class BarFrame: PictureFrame { } } +@available(SwiftStdlib 5.5, *) @SomeGlobalActor class BazFrame: NotIsolatedPictureFrame { init() { @@ -285,6 +304,7 @@ class BazFrame: NotIsolatedPictureFrame { class BazFrameIso: PictureFrame { // expected-error {{global actor 'SomeGlobalActor'-isolated class 'BazFrameIso' has different actor isolation from main actor-isolated superclass 'PictureFrame'}} } +@available(SwiftStdlib 5.5, *) func check() async { _ = await BarFrame() _ = await FooFrame() @@ -347,5 +367,3 @@ func testSender( sender.sendPtr(ptr) sender.sendStringArray(stringArray) } - -} // SwiftStdlib 5.5 diff --git a/test/Concurrency/actor_call_implicitly_async.swift b/test/Concurrency/actor_call_implicitly_async.swift index 14172a37f8e66..82c5eb9f2cd3f 100644 --- a/test/Concurrency/actor_call_implicitly_async.swift +++ b/test/Concurrency/actor_call_implicitly_async.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency -parse-as-library // REQUIRES: concurrency diff --git a/test/Concurrency/actor_isolation.swift b/test/Concurrency/actor_isolation.swift index a3aeb2b96541b..3f6d837e4e41a 100644 --- a/test/Concurrency/actor_isolation.swift +++ b/test/Concurrency/actor_isolation.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %target-swift-frontend -emit-module -emit-module-path %t/OtherActors.swiftmodule -module-name OtherActors %S/Inputs/OtherActors.swift -disable-availability-checking -// RUN: %target-typecheck-verify-swift -I %t -disable-availability-checking -warn-concurrency +// RUN: %target-typecheck-verify-swift -I %t -disable-availability-checking -warn-concurrency -parse-as-library // REQUIRES: concurrency import OtherActors // expected-remark{{add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'OtherActors'}}{{1-1=@preconcurrency }} diff --git a/test/Concurrency/concurrency_warnings.swift b/test/Concurrency/concurrency_warnings.swift index 31b90c7ba4889..6b8fc4610e874 100644 --- a/test/Concurrency/concurrency_warnings.swift +++ b/test/Concurrency/concurrency_warnings.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -warn-concurrency +// RUN: %target-typecheck-verify-swift -warn-concurrency -parse-as-library // REQUIRES: concurrency class GlobalCounter { diff --git a/test/Concurrency/concurrent_value_checking.swift b/test/Concurrency/concurrent_value_checking.swift index c392e2b67f294..3bc2c97c49cf1 100644 --- a/test/Concurrency/concurrent_value_checking.swift +++ b/test/Concurrency/concurrent_value_checking.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency +// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency -parse-as-library // REQUIRES: concurrency class NotConcurrent { } // expected-note 27{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}} diff --git a/test/Concurrency/property_initializers_swift6.swift b/test/Concurrency/property_initializers_swift6.swift index 14d39222ec481..21bf58638dbe5 100644 --- a/test/Concurrency/property_initializers_swift6.swift +++ b/test/Concurrency/property_initializers_swift6.swift @@ -3,17 +3,21 @@ // REQUIRES: asserts -@MainActor -func mainActorFn() -> Int { return 0 } // expected-note 2 {{calls to global function 'mainActorFn()' from outside of its actor context are implicitly asynchronous}} +@globalActor +actor GlobalActor { + static let shared = GlobalActor() +} + +@GlobalActor +func globalActorFn() -> Int { return 0 } // expected-note 2 {{calls to global function 'globalActorFn()' from outside of its actor context are implicitly asynchronous}} -@MainActor +@GlobalActor class C { - var x: Int = mainActorFn() // expected-error {{call to main actor-isolated global function 'mainActorFn()' in a synchronous nonisolated context}} + var x: Int = globalActorFn() // expected-error {{call to global actor 'GlobalActor'-isolated global function 'globalActorFn()' in a synchronous nonisolated context}} - lazy var y: Int = mainActorFn() + lazy var y: Int = globalActorFn() - static var z: Int = mainActorFn() + static var z: Int = globalActorFn() } -@MainActor -var x: Int = mainActorFn() // expected-error {{call to main actor-isolated global function 'mainActorFn()' in a synchronous nonisolated context}} +var x: Int = globalActorFn() // expected-error {{call to global actor 'GlobalActor'-isolated global function 'globalActorFn()' in a synchronous main actor-isolated context}} diff --git a/test/stmt/async.swift b/test/stmt/async.swift index a4319f4eaa040..16c75140593e2 100644 --- a/test/stmt/async.swift +++ b/test/stmt/async.swift @@ -2,9 +2,14 @@ // REQUIRES: concurrency -func f() async -> Int { 0 } +@_silgen_name("omnomInt") +func omnom(_ x: Int) -_ = await f() // expected-error{{'async' call in a function that does not support concurrency}} +func f() async -> Int { 0 } -async let y = await f() // expected-error{{'async let' in a function that does not support concurrency}} -// expected-error@-1{{'async' call in a function that does not support concurrency}} +func syncContext() { // expected-note 4 {{add 'async' to function 'syncContext()' to make it asynchronous}} + _ = await f() // expected-error{{'async' call in a function that does not support concurrency}} + async let y = await f() // expected-error{{'async let' in a function that does not support concurrency}} + // expected-error@-1{{'async' call in a function that does not support concurrency}} + await omnom(y) // expected-error{{'async let' in a function that does not support concurrency}} +} From 72e03b799dcecb343c2963328b42e90bce851332 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Mon, 14 Mar 2022 08:25:06 -0700 Subject: [PATCH 107/242] SE-0343 Update changelog for async top-level --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ed1b98b670f9..fcc01bb173ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ _**Note:** This is in reverse chronological order, so newer entries are added to ## Swift 5.7 +* [SE-0343][]: + +Top-level scripts support asynchronous calls. + +Using an `await` by calling an asynchronous function or accessing an isolated +variable transitions the top-level to an asynchronous context. As an +asynchronous context, top-level variables are `@MainActor`-isolated and the +top-level is run on the `@MainActor`. + +Note that the transition affects function overload resolution and starts an +implicit run loop to drive the concurrency machinery. + +Unmodified scripts are not affected by this change unless `-warn-concurrency` is +passed to the compiler invocation. With `-warn-concurrency`, variables in the +top-level are isolated to the main actor and the top-level context is isolated +to the main actor, but is not an asynchronous context. + * [SE-0336][]: It is now possible to declare `distributed actor` and `distributed func`s inside of them. @@ -9065,6 +9082,7 @@ Swift 1.0 [SE-0335]: [SE-0341]: [SE-0336]: +[SE-0343]: [SR-75]: [SR-106]: From 1afaf74f3c6ac6175f306b5faded0552b5d48f57 Mon Sep 17 00:00:00 2001 From: Evan Wilde Date: Tue, 15 Mar 2022 09:31:06 -0700 Subject: [PATCH 108/242] top-level is `@MainActor(unsafe)` below swift 6 Co-authored-by: Doug Gregor --- lib/AST/Decl.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 5a4f96d6a884d..652fdbcbd17a0 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -9090,7 +9090,9 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) { if (auto *tld = dyn_cast(dc)) { if (dc->isAsyncContext() || dc->getASTContext().LangOpts.WarnConcurrency) { if (Type mainActor = dc->getASTContext().getMainActorType()) - return ActorIsolation::forGlobalActor(mainActor, /*unsafe=*/false); + return ActorIsolation::forGlobalActor( + mainActor, + /*unsafe=*/!dc->getASTContext().isSwiftVersionAtLeast(6)); } } From 4796fb330e7cc0586daed62408d25d0827cc1829 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 15 Mar 2022 23:54:20 +0900 Subject: [PATCH 109/242] last bits of lingering _Distributed --- .../Runtime/distributed_actor_encode_roundtrip.swift | 2 +- test/Distributed/Runtime/distributed_actor_hop_to.swift | 2 +- test/Distributed/Runtime/distributed_actor_remoteCall.swift | 2 +- ...stributed_actor_remoteCallTarget_demanglingTargetNames.swift | 2 +- test/Distributed/distributed_actor_implicit_codable.swift | 2 +- test/Distributed/distributed_actor_system_missing.swift | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/Distributed/Runtime/distributed_actor_encode_roundtrip.swift b/test/Distributed/Runtime/distributed_actor_encode_roundtrip.swift index f5be7aaa48dfc..4f5a9a9cc4678 100644 --- a/test/Distributed/Runtime/distributed_actor_encode_roundtrip.swift +++ b/test/Distributed/Runtime/distributed_actor_encode_roundtrip.swift @@ -13,7 +13,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed import FakeDistributedActorSystems import FakeCodableForDistributedTests diff --git a/test/Distributed/Runtime/distributed_actor_hop_to.swift b/test/Distributed/Runtime/distributed_actor_hop_to.swift index d0c04ea50c281..96167689f5f77 100644 --- a/test/Distributed/Runtime/distributed_actor_hop_to.swift +++ b/test/Distributed/Runtime/distributed_actor_hop_to.swift @@ -15,7 +15,7 @@ // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/Runtime/distributed_actor_remoteCall.swift b/test/Distributed/Runtime/distributed_actor_remoteCall.swift index 74bde0ec1f61d..498560c49ecc7 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCall.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCall.swift @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed final class Obj: @unchecked Sendable, Codable {} diff --git a/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift b/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift index 7c0a73d45d873..63185ffa7d762 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift @@ -14,7 +14,7 @@ // FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574 // UNSUPPORTED: windows -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem diff --git a/test/Distributed/distributed_actor_implicit_codable.swift b/test/Distributed/distributed_actor_implicit_codable.swift index fb97920131146..ef11685d20558 100644 --- a/test/Distributed/distributed_actor_implicit_codable.swift +++ b/test/Distributed/distributed_actor_implicit_codable.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeActorSystem diff --git a/test/Distributed/distributed_actor_system_missing.swift b/test/Distributed/distributed_actor_system_missing.swift index 221097ddf4782..b0297d34e5ebb 100644 --- a/test/Distributed/distributed_actor_system_missing.swift +++ b/test/Distributed/distributed_actor_system_missing.swift @@ -4,7 +4,7 @@ // REQUIRES: concurrency // REQUIRES: distributed -import _Distributed +import Distributed distributed actor DA { // expected-error@-1{{distributed actor 'DA' does not declare ActorSystem it can be used with.}} From 8a1c5139938766922d98953f78c376f07c1758c9 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 16 Mar 2022 08:28:06 +0900 Subject: [PATCH 110/242] add test and availability annotations --- .../LocalTestingDistributedActorSystem.swift | 54 ++++++++++--------- .../distributed_actor_localSystem.swift | 36 +++++++++++++ 2 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 test/Distributed/Runtime/distributed_actor_localSystem.swift diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index 37b22c0b141b5..48502dcfdf21e 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -20,24 +20,11 @@ import Glibc import WinSDK #endif -public struct LocalTestingActorAddress: Hashable, Sendable, Codable { - public let address: String - - public init(parse address: String) { - self.address = address - } - - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - self.address = try container.decode(String.self) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(self.address) - } -} - +/// A `DistributedActorSystem` designed for local only testing. +/// +/// It will crash on any attempt of remote communication, but can be useful +/// for learning about `distributed actor` isolation, as well as early +/// prototyping stages of development where a real system is not necessary yet. @available(SwiftStdlib 5.7, *) public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable { public typealias ActorID = LocalTestingActorAddress @@ -86,9 +73,6 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ } public func resignID(_ id: ActorID) { - guard self.assignedIDsLock.withLock({ self.assignedIDs.contains(id) }) else { - fatalError("Attempted to resign unknown id '\(id)'") - } self.activeActorsLock.withLock { self.activeActors.removeValue(forKey: id) } @@ -109,7 +93,7 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ Act.ID == ActorID, Err: Error, Res: SerializationRequirement { - fatalError("Attempted to make remote call on actor \(actor) in a local-only actor system") + fatalError("Attempted to make remote call to \(target) on actor \(actor) using a local-only actor system") } public func remoteCallVoid( @@ -121,7 +105,7 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ where Act: DistributedActor, Act.ID == ActorID, Err: Error { - fatalError("Attempted to make remote call on actor \(actor) in a local-only actor system") + fatalError("Attempted to make remote call to \(target) on actor \(actor) using a local-only actor system") } private struct ActorIDProvider { @@ -140,6 +124,26 @@ public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @ } } +@available(SwiftStdlib 5.7, *) +public struct LocalTestingActorAddress: Hashable, Sendable, Codable { + public let address: String + + public init(parse address: String) { + self.address = address + } + + public init(from decoder: Decoder) throws { + let container = try decoder.singleValueContainer() + self.address = try container.decode(String.self) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(self.address) + } +} + +@available(SwiftStdlib 5.7, *) public struct LocalTestingInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable @@ -164,7 +168,8 @@ public struct LocalTestingInvocationEncoder: DistributedTargetInvocationEncoder } } -public class LocalTestingInvocationDecoder : DistributedTargetInvocationDecoder { +@available(SwiftStdlib 5.7, *) +public final class LocalTestingInvocationDecoder : DistributedTargetInvocationDecoder { public typealias SerializationRequirement = Codable public func decodeGenericSubstitutions() throws -> [Any.Type] { @@ -197,6 +202,7 @@ public struct LocalTestingDistributedActorSystemError: DistributedActorSystemErr // === lock ---------------------------------------------------------------- +@available(SwiftStdlib 5.7, *) fileprivate class _Lock { #if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) private let underlying: UnsafeMutablePointer diff --git a/test/Distributed/Runtime/distributed_actor_localSystem.swift b/test/Distributed/Runtime/distributed_actor_localSystem.swift new file mode 100644 index 0000000000000..fc9206bd022cf --- /dev/null +++ b/test/Distributed/Runtime/distributed_actor_localSystem.swift @@ -0,0 +1,36 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -module-name main -Xfrontend -enable-experimental-distributed -Xfrontend -disable-availability-checking -j2 -parse-as-library -I %t %s -o %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s --color + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: distributed + +// rdar://76038845 +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: back_deployment_runtime + +import _Distributed + +distributed actor Worker { + typealias ActorSystem = LocalTestingDistributedActorSystem + + distributed func hi() { + print("hi!") + } + + nonisolated var description: Swift.String { + "Worker(\(id))" + } +} + +// ==== Execute ---------------------------------------------------------------- +@main struct Main { + static func main() async throws { + let system = LocalTestingDistributedActorSystem() + + let actor = Worker(system: system) + try await actor.hi() // local calls should still just work + // CHECK: hi! + } +} From 683224acd438fe924759dd9ae45d4220be713795 Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 15 Mar 2022 17:00:51 -0700 Subject: [PATCH 111/242] Revert "Separate the C++ and Objective-C generated header output types." This reverts commit c7a5049a28a0c4be034ccdd765bf45640a2fe3da. --- include/swift/Basic/FileTypes.def | 1 - lib/Basic/FileTypes.cpp | 3 --- lib/Driver/Driver.cpp | 4 +--- lib/Driver/ToolChains.cpp | 2 -- lib/Frontend/ArgsToFrontendOutputsConverter.cpp | 4 ++-- 5 files changed, 3 insertions(+), 11 deletions(-) diff --git a/include/swift/Basic/FileTypes.def b/include/swift/Basic/FileTypes.def index b4490c07c30be..074df9e4b6ab1 100644 --- a/include/swift/Basic/FileTypes.def +++ b/include/swift/Basic/FileTypes.def @@ -60,7 +60,6 @@ TYPE("llvm-ir", LLVM_IR, "ll", "") TYPE("llvm-bc", LLVM_BC, "bc", "") TYPE("diagnostics", SerializedDiagnostics, "dia", "") TYPE("objc-header", ObjCHeader, "h", "") -TYPE("cxx-header", CXXHeader, "h", "") TYPE("swift-dependencies", SwiftDeps, "swiftdeps", "") TYPE("external-swift-dependencies", ExternalSwiftDeps, "swiftdeps.external", "") TYPE("remap", Remapping, "remap", "") diff --git a/lib/Basic/FileTypes.cpp b/lib/Basic/FileTypes.cpp index e8d6f5c50ec7c..58db293c5d090 100644 --- a/lib/Basic/FileTypes.cpp +++ b/lib/Basic/FileTypes.cpp @@ -74,7 +74,6 @@ bool file_types::isTextual(ID Id) { case file_types::TY_RawSIL: case file_types::TY_LLVM_IR: case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: case file_types::TY_AutolinkFile: case file_types::TY_ImportedModules: case file_types::TY_TBD: @@ -133,7 +132,6 @@ bool file_types::isAfterLLVM(ID Id) { case file_types::TY_ASTDump: case file_types::TY_RawSIL: case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: case file_types::TY_AutolinkFile: case file_types::TY_Image: case file_types::TY_dSYM: @@ -184,7 +182,6 @@ bool file_types::isPartOfSwiftCompilation(ID Id) { case file_types::TY_Object: case file_types::TY_Dependencies: case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: case file_types::TY_AutolinkFile: case file_types::TY_PCH: case file_types::TY_ImportedModules: diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 02d9cf654016c..f513171f82bd5 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1981,8 +1981,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, if (Arg *A = Args.getLastArg(options::OPT_import_objc_header)) { StringRef Value = A->getValue(); auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value)); - if (Ty == file_types::TY_ObjCHeader || - Ty == file_types::TY_CXXHeader) { + if (Ty == file_types::TY_ObjCHeader) { auto *HeaderInput = C.createAction(*A, Ty); StringRef PersistentPCHDir; if (const Arg *A = Args.getLastArg(options::OPT_pch_output_dir)) { @@ -2066,7 +2065,6 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, case file_types::TY_LLVM_BC: case file_types::TY_SerializedDiagnostics: case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: case file_types::TY_ClangModuleFile: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index aaa7a6b7299c8..40bbcc7b1aed1 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -676,7 +676,6 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const { case file_types::TY_SwiftModuleDocFile: case file_types::TY_SerializedDiagnostics: case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -937,7 +936,6 @@ ToolChain::constructInvocation(const BackendJobAction &job, case file_types::TY_SwiftModuleDocFile: case file_types::TY_SerializedDiagnostics: case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index 7095d391129d6..d50ad19069296 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -444,7 +444,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( auto cxxHeaderOutputPath = determineSupplementaryOutputFilename( OPT_emit_cxx_header, pathsFromArguments.CxxHeaderOutputPath, - file_types::TY_CXXHeader, "", + file_types::TY_ObjCHeader, "", defaultSupplementaryOutputPathExcludingExtension); auto loadedModuleTracePath = determineSupplementaryOutputFilename( @@ -587,7 +587,7 @@ createFromTypeToPathMap(const TypeToPathMap *map) { return paths; const std::pair typesAndStrings[] = { {file_types::TY_ObjCHeader, paths.ObjCHeaderOutputPath}, - {file_types::TY_CXXHeader, paths.CxxHeaderOutputPath}, + {file_types::TY_ObjCHeader, paths.CxxHeaderOutputPath}, {file_types::TY_SwiftModuleFile, paths.ModuleOutputPath}, {file_types::TY_SwiftModuleDocFile, paths.ModuleDocOutputPath}, {file_types::TY_SwiftSourceInfoFile, paths.ModuleSourceInfoOutputPath}, From b6035517e4aa0e6ec4030324ccd1eeb9f047fb5b Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Tue, 15 Mar 2022 21:07:43 -0700 Subject: [PATCH 112/242] Print Variadic Sequence Types with Array Slice Sugar in SIL Mode The SIL parser cannot handle 'T...'. Use '[T]' instead. --- lib/AST/ASTPrinter.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/AST/ASTPrinter.cpp b/lib/AST/ASTPrinter.cpp index 59e587b8253a0..43108fc5ad6ca 100644 --- a/lib/AST/ASTPrinter.cpp +++ b/lib/AST/ASTPrinter.cpp @@ -6196,8 +6196,14 @@ class TypePrinter : public TypeVisitor { } void visitVariadicSequenceType(VariadicSequenceType *T) { - visit(T->getBaseType()); - Printer << "..."; + if (Options.PrintForSIL) { + Printer << "["; + visit(T->getBaseType()); + Printer << "]"; + } else { + visit(T->getBaseType()); + Printer << "..."; + } } void visitProtocolType(ProtocolType *T) { From 1afdb4727137ee4310fe3a03c76095370ed9f8cd Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 10 Mar 2022 10:46:41 -0800 Subject: [PATCH 113/242] Un-XFAIL Stdlib Validation Test --- validation-test/SIL/parse_stdlib.sil | 1 - 1 file changed, 1 deletion(-) diff --git a/validation-test/SIL/parse_stdlib.sil b/validation-test/SIL/parse_stdlib.sil index 751d275837c0f..7b373654d4216 100644 --- a/validation-test/SIL/parse_stdlib.sil +++ b/validation-test/SIL/parse_stdlib.sil @@ -3,4 +3,3 @@ // RUN: %target-sil-opt -enable-sil-verify-all=true %t.sil > /dev/null // REQUIRES: long_test // REQUIRES: nonexecutable_test -// REQUIRES: rdar81658645 From 6b64ac26f40b1b2f330f50756dc7fdac99487630 Mon Sep 17 00:00:00 2001 From: Holly Borla Date: Tue, 15 Mar 2022 20:59:51 -0700 Subject: [PATCH 114/242] [RequirementMachine] Suppress redundant requirement warnings for inferred requirements. --- lib/AST/RequirementMachine/HomotopyReduction.cpp | 12 ++++++++++-- lib/AST/RequirementMachine/RewriteSystem.cpp | 4 ++++ lib/AST/RequirementMachine/Rule.h | 1 - .../requirement_machine_diagnostics.swift | 16 ++++++++++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/AST/RequirementMachine/HomotopyReduction.cpp b/lib/AST/RequirementMachine/HomotopyReduction.cpp index baf916ccc1aa3..78b9aafa74a80 100644 --- a/lib/AST/RequirementMachine/HomotopyReduction.cpp +++ b/lib/AST/RequirementMachine/HomotopyReduction.cpp @@ -136,8 +136,16 @@ void RewriteSystem::propagateRedundantRequirementIDs() { MutableTerm lhs(rule.getLHS()); for (auto ruleID : rewritePath.getRulesInEmptyContext(lhs, *this)) { auto &replacement = Rules[ruleID]; - if (!replacement.isPermanent() && - !replacement.getRequirementID().hasValue()) { + if (!replacement.isPermanent()) { + // If the replacement rule already has a requirementID, overwrite + // it if the existing ID corresponds to an inferred requirement. + // This effectively makes the inferred requirement the redundant + // one, which makes it easier to suppress redundancy warnings for + // inferred requirements later on. + auto existingID = replacement.getRequirementID(); + if (existingID.hasValue() && !WrittenRequirements[*existingID].inferred) + continue; + if (Debug.contains(DebugFlags::PropagateRequirementIDs)) { llvm::dbgs() << "\n- propagating ID = " << requirementID diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp index bad0a7c6027ad..565e6162969e8 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.cpp +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -571,6 +571,10 @@ void RewriteSystem::computeRedundantRequirementDiagnostics( auto requirement = WrittenRequirements[requirementID]; auto pairIt = rulesPerRequirement.find(requirementID); + // Inferred requirements can be re-stated without warning. + if (requirement.inferred) + continue; + // If there are no rules for this structural requirement, then // the requirement was never added to the rewrite system because // it is trivially redundant. diff --git a/lib/AST/RequirementMachine/Rule.h b/lib/AST/RequirementMachine/Rule.h index 193264c3fb479..f4c623b2d0b16 100644 --- a/lib/AST/RequirementMachine/Rule.h +++ b/lib/AST/RequirementMachine/Rule.h @@ -97,7 +97,6 @@ class Rule final { } void setRequirementID(Optional requirementID) { - assert(!getRequirementID().hasValue()); this->requirementID = requirementID; } diff --git a/test/Generics/requirement_machine_diagnostics.swift b/test/Generics/requirement_machine_diagnostics.swift index a977698662a53..9b6b1ea7935c9 100644 --- a/test/Generics/requirement_machine_diagnostics.swift +++ b/test/Generics/requirement_machine_diagnostics.swift @@ -178,3 +178,19 @@ class X3 { } func sameTypeConcrete2(_: T) where T.B : X3, T.C == T.B, T.C == X3 { } // expected-warning@-1{{redundant superclass constraint 'T.B' : 'X3'}} + + +// Redundant requirement warnings are suppressed for inferred requirements. + +protocol P11 { + associatedtype X + associatedtype Y + associatedtype Z +} + +func inferred1(_: Set) {} +func inferred2(_: Set) where T: Hashable {} +func inferred3(_: T) where T.X : Hashable, T.Z == Set, T.X == T.Y {} +func inferred4(_: T) where T.Z == Set, T.X : Hashable, T.X == T.Y {} +func inferred5(_: T) where T.Z == Set, T.Y : Hashable, T.X == T.Y {} +func inferred6(_: T) where T.Y : Hashable, T.Z == Set, T.X == T.Y {} From 4c09b9121de0bc210e461f3d9c699d05eb077653 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 16 Mar 2022 13:45:27 +0900 Subject: [PATCH 115/242] update Distributed module import --- test/Distributed/Runtime/distributed_actor_localSystem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Distributed/Runtime/distributed_actor_localSystem.swift b/test/Distributed/Runtime/distributed_actor_localSystem.swift index fc9206bd022cf..2f0fa92786486 100644 --- a/test/Distributed/Runtime/distributed_actor_localSystem.swift +++ b/test/Distributed/Runtime/distributed_actor_localSystem.swift @@ -10,7 +10,7 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime -import _Distributed +import Distributed distributed actor Worker { typealias ActorSystem = LocalTestingDistributedActorSystem From af443bee0a741f29f55dbd2279c019413cb479ae Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 23:50:19 -0400 Subject: [PATCH 116/242] RequirementMachine: Implement RewriteContext::getRequirementMachine(ProtocolDecl *) --- lib/AST/RequirementMachine/RewriteContext.cpp | 116 +++++++++++++++--- lib/AST/RequirementMachine/RewriteContext.h | 20 ++- 2 files changed, 119 insertions(+), 17 deletions(-) diff --git a/lib/AST/RequirementMachine/RewriteContext.cpp b/lib/AST/RequirementMachine/RewriteContext.cpp index 0159fb8fb6b0a..084423c2a34b9 100644 --- a/lib/AST/RequirementMachine/RewriteContext.cpp +++ b/lib/AST/RequirementMachine/RewriteContext.cpp @@ -133,8 +133,7 @@ RequirementMachine *RewriteContext::getRequirementMachine( auto *newMachine = new rewriting::RequirementMachine(*this); machine = newMachine; - // This might re-entrantly invalidate 'machine', which is a reference - // into Protos. + // This might re-entrantly invalidate 'machine'. auto status = newMachine->initWithGenericSignature(sig); newMachine->checkCompletionResult(status.first); @@ -170,7 +169,10 @@ void RewriteContext::getProtocolComponentRec( stack.push_back(proto); // Look at each successor. - for (auto *depProto : proto->getProtocolDependencies()) { + auto found = Dependencies.find(proto); + assert(found != Dependencies.end()); + + for (auto *depProto : found->second) { auto found = Protos.find(depProto); if (found == Protos.end()) { // Successor has not yet been visited. Recurse. @@ -223,40 +225,121 @@ void RewriteContext::getProtocolComponentRec( } } -/// Lazily construct a requirement machine for the given protocol's strongly -/// connected component (SCC) in the protocol dependency graph. +/// Get the strongly connected component (SCC) of the protocol dependency +/// graph containing the given protocol. /// -/// This can only be called once, to prevent multiple requirement machines -/// for being built with the same component. -ArrayRef RewriteContext::getProtocolComponent( - const ProtocolDecl *proto) { +/// You must not hold on to this reference across calls to any other +/// Requirement Machine operations, since they might insert new entries +/// into the underlying DenseMap, invalidating the reference. +RewriteContext::ProtocolComponent & +RewriteContext::getProtocolComponentImpl(const ProtocolDecl *proto) { + { + // We pre-load protocol dependencies into the Dependencies map + // because getProtocolDependencies() can trigger recursive calls into + // the requirement machine in highly-invalid code, which violates + // invariants in getProtocolComponentRec(). + SmallVector worklist; + worklist.push_back(proto); + + while (!worklist.empty()) { + const auto *otherProto = worklist.back(); + worklist.pop_back(); + + auto found = Dependencies.find(otherProto); + if (found != Dependencies.end()) + continue; + + auto protoDeps = otherProto->getProtocolDependencies(); + Dependencies.insert(std::make_pair(otherProto, protoDeps)); + for (auto *nextProto : protoDeps) + worklist.push_back(nextProto); + } + } + auto found = Protos.find(proto); if (found == Protos.end()) { + if (ProtectProtocolComponentRec) { + llvm::errs() << "Too much recursion is bad\n"; + abort(); + } + + ProtectProtocolComponentRec = true; + SmallVector stack; getProtocolComponentRec(proto, stack); assert(stack.empty()); found = Protos.find(proto); assert(found != Protos.end()); + + ProtectProtocolComponentRec = false; } assert(Components.count(found->second.ComponentID) != 0); auto &component = Components[found->second.ComponentID]; - if (component.InProgress) { - llvm::errs() << "Re-entrant construction of requirement " - << "machine for:"; + assert(std::find(component.Protos.begin(), component.Protos.end(), proto) + != component.Protos.end() && "Protocol is in the wrong SCC"); + return component; +} + +/// Get the list of protocols in the strongly connected component (SCC) +/// of the protocol dependency graph containing the given protocol. +/// +/// This can only be called once, to prevent multiple requirement machines +/// for being built with the same component. +ArrayRef RewriteContext::getProtocolComponent( + const ProtocolDecl *proto) { + auto &component = getProtocolComponentImpl(proto); + + if (component.ComputingRequirementSignatures) { + llvm::errs() << "Re-entrant minimization of requirement signatures for: "; for (auto *proto : component.Protos) llvm::errs() << " " << proto->getName(); llvm::errs() << "\n"; abort(); } - component.InProgress = true; + component.ComputingRequirementSignatures = true; return component.Protos; } +/// Get the list of protocols in the strongly connected component (SCC) +/// of the protocol dependency graph containing the given protocol. +/// +/// This can only be called once, to prevent multiple requirement machines +/// for being built with the same component. +RequirementMachine *RewriteContext::getRequirementMachine( + const ProtocolDecl *proto) { + auto &component = getProtocolComponentImpl(proto); + + if (component.Machine) { + if (!component.Machine->isComplete()) { + llvm::errs() << "Re-entrant construction of requirement machine for: "; + for (auto *proto : component.Protos) + llvm::errs() << " " << proto->getName(); + llvm::errs() << "\n"; + abort(); + } + + return component.Machine; + } + + // Store this requirement machine before adding the protocols, to catch + // re-entrant construction via initWithProtocolSignatureRequirements() + // below. + auto *newMachine = new rewriting::RequirementMachine(*this); + component.Machine = newMachine; + + // This might re-entrantly invalidate 'component.Machine'. + auto status = newMachine->initWithProtocolSignatureRequirements( + component.Protos); + newMachine->checkCompletionResult(status.first); + + return newMachine; +} + bool RewriteContext::isRecursivelyConstructingRequirementMachine( const ProtocolDecl *proto) { if (proto->isRequirementSignatureComputed()) @@ -270,12 +353,17 @@ bool RewriteContext::isRecursivelyConstructingRequirementMachine( if (component == Components.end()) return false; - return component->second.InProgress; + return component->second.ComputingRequirementSignatures; } /// We print stats in the destructor, which should get executed at the end of /// a compilation job. RewriteContext::~RewriteContext() { + for (const auto &pair : Components) + delete pair.second.Machine; + + Components.clear(); + for (const auto &pair : Machines) delete pair.second; diff --git a/lib/AST/RequirementMachine/RewriteContext.h b/lib/AST/RequirementMachine/RewriteContext.h index 67843f758f291..00fc1d04dadd0 100644 --- a/lib/AST/RequirementMachine/RewriteContext.h +++ b/lib/AST/RequirementMachine/RewriteContext.h @@ -86,16 +86,28 @@ class RewriteContext final { /// The members of this connected component. ArrayRef Protos; - /// Each connected component has a lazily-created requirement machine. - bool InProgress = false; + /// Whether we are currently computing the requirement signatures of + /// the protocols in this component. + bool ComputingRequirementSignatures = false; + + /// Each connected component has a lazily-created requirement machine + /// built from the requirement signatures of the protocols in this + /// component. + RequirementMachine *Machine = nullptr; }; - /// The protocol dependency graph. + /// We pre-load protocol dependencies here to avoid re-entrancy. + llvm::DenseMap> Dependencies; + + /// Maps protocols to their connected components. llvm::DenseMap Protos; /// Used by Tarjan's algorithm. unsigned NextComponentIndex = 0; + /// Prevents re-entrant calls into getProtocolComponentRec(). + bool ProtectProtocolComponentRec = false; + /// The connected components. Keys are the ComponentID fields of /// ProtocolNode. llvm::DenseMap Components; @@ -111,6 +123,7 @@ class RewriteContext final { void getProtocolComponentRec(const ProtocolDecl *proto, SmallVectorImpl &stack); + ProtocolComponent &getProtocolComponentImpl(const ProtocolDecl *proto); public: /// Statistics. @@ -182,6 +195,7 @@ class RewriteContext final { bool isRecursivelyConstructingRequirementMachine(CanGenericSignature sig); ArrayRef getProtocolComponent(const ProtocolDecl *proto); + RequirementMachine *getRequirementMachine(const ProtocolDecl *proto); bool isRecursivelyConstructingRequirementMachine(const ProtocolDecl *proto); ~RewriteContext(); From 327e7e580b75eccb3b6997ef9c9456bbe465fb07 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 23:54:28 -0400 Subject: [PATCH 117/242] RequirementMachine: Add importedRules parameter to RewriteSystem::initialize() --- .../RequirementMachine/RequirementLowering.h | 5 ++ .../RequirementMachine/RequirementMachine.cpp | 4 ++ lib/AST/RequirementMachine/RewriteSystem.cpp | 62 +++++++++++++++++++ lib/AST/RequirementMachine/RewriteSystem.h | 6 +- 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.h b/lib/AST/RequirementMachine/RequirementLowering.h index d331cf6e7a00e..d468d789259ac 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.h +++ b/lib/AST/RequirementMachine/RequirementLowering.h @@ -20,6 +20,7 @@ #include #include "Diagnostics.h" #include "RewriteContext.h" +#include "Rule.h" #include "Symbol.h" #include "Term.h" @@ -93,6 +94,10 @@ struct RuleBuilder { /// being imported. std::vector ProtocolsToImport; + /// The rules representing a complete rewrite system for the above vector, + /// pulled in by collectRulesFromReferencedProtocols(). + std::vector ImportedRules; + /// New rules to add which will be marked 'permanent'. These are rules for /// introducing associated types, and relationships between layout, /// superclass and concrete type symbols. They are not eliminated by diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp index 9d7271dde535e..6c13a668f0776 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.cpp +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -91,6 +91,7 @@ RequirementMachine::initWithProtocolSignatureRequirements( // Add the initial set of rewrite rules to the rewrite system. System.initialize(/*recordLoops=*/true, protos, std::move(builder.WrittenRequirements), + std::move(builder.ImportedRules), std::move(builder.PermanentRules), std::move(builder.RequirementRules)); @@ -137,6 +138,7 @@ RequirementMachine::initWithGenericSignature(CanGenericSignature sig) { System.initialize(/*recordLoops=*/false, /*protos=*/ArrayRef(), std::move(builder.WrittenRequirements), + std::move(builder.ImportedRules), std::move(builder.PermanentRules), std::move(builder.RequirementRules)); @@ -181,6 +183,7 @@ RequirementMachine::initWithProtocolWrittenRequirements( // Add the initial set of rewrite rules to the rewrite system. System.initialize(/*recordLoops=*/true, protos, std::move(builder.WrittenRequirements), + std::move(builder.ImportedRules), std::move(builder.PermanentRules), std::move(builder.RequirementRules)); @@ -229,6 +232,7 @@ RequirementMachine::initWithWrittenRequirements( System.initialize(/*recordLoops=*/true, /*protos=*/ArrayRef(), std::move(builder.WrittenRequirements), + std::move(builder.ImportedRules), std::move(builder.PermanentRules), std::move(builder.RequirementRules)); diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp index bad0a7c6027ad..4252b052a4361 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.cpp +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -37,9 +37,41 @@ RewriteSystem::~RewriteSystem() { Context.RuleTrieRootHistogram); } +/// Initialize the rewrite system using rewrite rules built by the RuleBuilder. +/// +/// - recordLoops: Whether this is a rewrite system built from user-written +/// requirements, in which case we will perform minimization using rewrite +/// loops recorded during completion. +/// +/// - protos: If this is a rewrite system built from a protocol connected +/// component, this contains the members of the protocol. For a rewrite +/// system built from a generic signature, this is empty. Used by +/// RewriteSystem::isInMinimizationDomain(). +/// +/// These parameters should be populated from the corresponding fields of the +/// RuleBuilder instance: +/// +/// - writtenRequirements: The user-written requirements, if any, used to +/// track source locations for redundancy diagnostics. +/// +/// - importedRules: Rewrite rules for referenced protocols. These come from +/// the Requirement Machine instances for these protocols' connected +/// components, so they are already confluent and can be imported verbatim. +/// +/// - permanentRules: Permanent rules, such as associated type introduction +/// rules for associated types defined in protocols in this connected +/// component. +/// +/// - requirementRules: Rules corresponding to generic requirements written +/// by the user. +/// +/// This can only be called once. It adds the rules to the rewrite system, +/// allowing computeConfluentCompletion() to be called to compute the +/// complete rewrite system. void RewriteSystem::initialize( bool recordLoops, ArrayRef protos, ArrayRef writtenRequirements, + std::vector &&importedRules, std::vector> &&permanentRules, std::vector>> &&requirementRules) { @@ -50,6 +82,36 @@ void RewriteSystem::initialize( Protos = protos; WrittenRequirements = writtenRequirements; + // Pre-populate our rules vector with the list of imported rules, and note + // the position of the first local (not imported) rule. + Rules = std::move(importedRules); + FirstLocalRule = Rules.size(); + + // Add the imported rules to the trie. + for (unsigned newRuleID : indices(Rules)) { + const auto &newRule = Rules[newRuleID]; + // Skip simplified rules. At the very least we need to skip RHS-simplified + // rules since their left hand sides might duplicate existing rules; the + // others are skipped purely as an optimization. + if (newRule.isLHSSimplified() || + newRule.isRHSSimplified() || + newRule.isSubstitutionSimplified()) + continue; + + auto oldRuleID = Trie.insert(newRule.getLHS().begin(), + newRule.getLHS().end(), + newRuleID); + if (oldRuleID) { + llvm::errs() << "Imported rules have duplicate left hand sides!\n"; + llvm::errs() << "New rule #" << newRuleID << ": " << newRule << "\n"; + const auto &oldRule = getRule(*oldRuleID); + llvm::errs() << "Old rule #" << *oldRuleID << ": " << oldRule << "\n\n"; + dump(llvm::errs()); + abort(); + } + } + + // Now add our own rules. for (const auto &rule : permanentRules) addPermanentRule(rule.first, rule.second); diff --git a/lib/AST/RequirementMachine/RewriteSystem.h b/lib/AST/RequirementMachine/RewriteSystem.h index 7141b9221e55f..06cdc1ea92189 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.h +++ b/lib/AST/RequirementMachine/RewriteSystem.h @@ -72,6 +72,8 @@ class RewriteSystem final { /// as rules introduced by the completion procedure. std::vector Rules; + unsigned FirstLocalRule = 0; + /// A prefix trie of rule left hand sides to optimize lookup. The value /// type is an index into the Rules array defined above. Trie Trie; @@ -122,8 +124,10 @@ class RewriteSystem final { DebugOptions getDebugOptions() const { return Debug; } - void initialize(bool recordLoops, ArrayRef protos, + void initialize(bool recordLoops, + ArrayRef protos, ArrayRef writtenRequirements, + std::vector &&importedRules, std::vector> &&permanentRules, std::vector>> &&requirementRules); From c08601bee909a2eb4c2d281f536fa79cdd273a62 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 15 Mar 2022 00:08:04 -0400 Subject: [PATCH 118/242] RequirementMachine: Implement RewriteSystem::getLocalRules() --- lib/AST/RequirementMachine/RequirementMachine.cpp | 4 ++++ lib/AST/RequirementMachine/RequirementMachine.h | 2 ++ lib/AST/RequirementMachine/RewriteSystem.h | 9 +++++++++ 3 files changed, 15 insertions(+) diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp index 6c13a668f0776..19d8bfcfd1912 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.cpp +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -335,6 +335,10 @@ std::string RequirementMachine::getRuleAsStringForDiagnostics( return out.str(); } +ArrayRef RequirementMachine::getLocalRules() const { + return System.getLocalRules(); +} + bool RequirementMachine::isComplete() const { return Complete; } diff --git a/lib/AST/RequirementMachine/RequirementMachine.h b/lib/AST/RequirementMachine/RequirementMachine.h index f236ffa80d1c3..a224eaed7ef7b 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.h +++ b/lib/AST/RequirementMachine/RequirementMachine.h @@ -158,6 +158,8 @@ class RequirementMachine final { std::vector computeMinimalGenericSignatureRequirements(bool reconstituteSugar); + ArrayRef getLocalRules() const; + std::string getRuleAsStringForDiagnostics(unsigned ruleID) const; GenericSignatureErrors getErrors() const; diff --git a/lib/AST/RequirementMachine/RewriteSystem.h b/lib/AST/RequirementMachine/RewriteSystem.h index 06cdc1ea92189..efef422471be2 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.h +++ b/lib/AST/RequirementMachine/RewriteSystem.h @@ -144,10 +144,19 @@ class RewriteSystem final { return (unsigned)(&rule - &*Rules.begin()); } + /// Get an array of all rewrite rules. ArrayRef getRules() const { return Rules; } + /// Get an array of rewrite rules, not including rewrite rules imported + /// from referenced protocols. + ArrayRef getLocalRules() const { + return getRules().slice(FirstLocalRule); + } + + /// Get the rewrite rule at the given index. Note that this is an index + /// into getRules(), *NOT* getLocalRules(). Rule &getRule(unsigned ruleID) { return Rules[ruleID]; } From 97041683515c56dfcb385a8b56005647cb89ef47 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 14 Mar 2022 23:51:24 -0400 Subject: [PATCH 119/242] RequirementMachine: Skip imported rules in various places when iterating over all rules --- lib/AST/RequirementMachine/HomotopyReduction.cpp | 9 ++++++--- lib/AST/RequirementMachine/KnuthBendix.cpp | 2 +- lib/AST/RequirementMachine/RewriteSystem.cpp | 9 +++++---- lib/AST/RequirementMachine/SimplifySubstitutions.cpp | 2 +- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/AST/RequirementMachine/HomotopyReduction.cpp b/lib/AST/RequirementMachine/HomotopyReduction.cpp index baf916ccc1aa3..ac57d93d58f2b 100644 --- a/lib/AST/RequirementMachine/HomotopyReduction.cpp +++ b/lib/AST/RequirementMachine/HomotopyReduction.cpp @@ -682,7 +682,8 @@ RewriteSystem::getMinimizedProtocolRules() const { assert(!Protos.empty()); llvm::DenseMap rules; - for (unsigned ruleID : indices(Rules)) { + for (unsigned ruleID = FirstLocalRule, e = Rules.size(); + ruleID < e; ++ruleID) { const auto &rule = getRule(ruleID); if (rule.isPermanent() || @@ -713,7 +714,8 @@ RewriteSystem::getMinimizedGenericSignatureRules() const { assert(Protos.empty()); std::vector rules; - for (unsigned ruleID : indices(Rules)) { + for (unsigned ruleID = FirstLocalRule, e = Rules.size(); + ruleID < e; ++ruleID) { const auto &rule = getRule(ruleID); if (rule.isPermanent() || @@ -768,7 +770,8 @@ void RewriteSystem::verifyMinimizedRules( const llvm::DenseSet &redundantConformances) const { unsigned redundantRuleCount = 0; - for (unsigned ruleID : indices(Rules)) { + for (unsigned ruleID = FirstLocalRule, e = Rules.size(); + ruleID < e; ++ruleID) { const auto &rule = getRule(ruleID); // Ignore the rewrite rule if it is not part of our minimization domain. diff --git a/lib/AST/RequirementMachine/KnuthBendix.cpp b/lib/AST/RequirementMachine/KnuthBendix.cpp index 4c241afb4fef4..471af4576dba5 100644 --- a/lib/AST/RequirementMachine/KnuthBendix.cpp +++ b/lib/AST/RequirementMachine/KnuthBendix.cpp @@ -301,7 +301,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount, ruleCount = Rules.size(); // For every rule, looking for other rules that overlap with this rule. - for (unsigned i = 0, e = Rules.size(); i < e; ++i) { + for (unsigned i = FirstLocalRule, e = Rules.size(); i < e; ++i) { const auto &lhs = getRule(i); if (lhs.isLHSSimplified() || lhs.isRHSSimplified() || diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp index 4252b052a4361..dd33d8b4b0bb3 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.cpp +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -337,7 +337,7 @@ bool RewriteSystem::addExplicitRule(MutableTerm lhs, MutableTerm rhs, void RewriteSystem::simplifyLeftHandSides() { assert(Complete); - for (unsigned ruleID = 0, e = Rules.size(); ruleID < e; ++ruleID) { + for (unsigned ruleID = FirstLocalRule, e = Rules.size(); ruleID < e; ++ruleID) { auto &rule = getRule(ruleID); if (rule.isLHSSimplified()) continue; @@ -380,7 +380,7 @@ void RewriteSystem::simplifyLeftHandSides() { void RewriteSystem::simplifyRightHandSides() { assert(Complete); - for (unsigned ruleID = 0, e = Rules.size(); ruleID < e; ++ruleID) { + for (unsigned ruleID = FirstLocalRule, e = Rules.size(); ruleID < e; ++ruleID) { auto &rule = getRule(ruleID); if (rule.isRHSSimplified()) continue; @@ -478,7 +478,7 @@ void RewriteSystem::verifyRewriteRules(ValidityPolicy policy) const { abort(); \ } - for (const auto &rule : Rules) { + for (const auto &rule : getLocalRules()) { const auto &lhs = rule.getLHS(); const auto &rhs = rule.getRHS(); @@ -585,7 +585,8 @@ void RewriteSystem::computeRedundantRequirementDiagnostics( // Collect non-explicit requirements that are not redundant. llvm::SmallDenseSet impliedRequirements; - for (unsigned ruleID : indices(getRules())) { + for (unsigned ruleID = FirstLocalRule, e = Rules.size(); + ruleID < e; ++ruleID) { auto &rule = getRules()[ruleID]; if (!rule.getRequirementID().hasValue() && diff --git a/lib/AST/RequirementMachine/SimplifySubstitutions.cpp b/lib/AST/RequirementMachine/SimplifySubstitutions.cpp index e4e9ccfca9f84..e04696a302966 100644 --- a/lib/AST/RequirementMachine/SimplifySubstitutions.cpp +++ b/lib/AST/RequirementMachine/SimplifySubstitutions.cpp @@ -378,7 +378,7 @@ RewriteSystem::simplifySubstitutions(Term baseTerm, Symbol symbol, /// is built, and a final simplification pass is performed with \p map set to /// the new property map. void RewriteSystem::simplifyLeftHandSideSubstitutions(const PropertyMap *map) { - for (unsigned ruleID = 0, e = Rules.size(); ruleID < e; ++ruleID) { + for (unsigned ruleID = FirstLocalRule, e = Rules.size(); ruleID < e; ++ruleID) { auto &rule = getRule(ruleID); if (rule.isSubstitutionSimplified()) continue; From 653977a51cafa783d1e8dd22a2e47e5d4b6a55d9 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 15 Mar 2022 00:08:26 -0400 Subject: [PATCH 120/242] RequirementMachine: Rule sharing All the pieces are now in place so that the RuleBuilder can assemble a confluent rewrite system for downstream protocol dependencies, instead of building rules from protocol requirement signatures. --- .../RequirementLowering.cpp | 34 +++++++++++++------ test/Generics/rdar79564324.swift | 6 ++-- test/Generics/rdar90219229.swift | 10 +++--- test/Generics/unify_superclass_types_1.swift | 2 +- test/Generics/unify_superclass_types_2.swift | 2 +- test/Generics/unify_superclass_types_3.swift | 4 +-- test/Generics/unify_superclass_types_4.swift | 8 ++--- 7 files changed, 39 insertions(+), 27 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 9d34338007133..4058ddcf779a1 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -33,6 +33,7 @@ #include "swift/AST/TypeMatcher.h" #include "swift/AST/TypeRepr.h" #include "llvm/ADT/SmallVector.h" +#include "RequirementMachine.h" #include "RewriteContext.h" #include "RewriteSystem.h" #include "Symbol.h" @@ -1403,21 +1404,32 @@ void RuleBuilder::collectRulesFromReferencedProtocols() { // if this is a rewrite system for a connected component of the protocol // dependency graph, add rewrite rules for each referenced protocol not part // of this connected component. + + // First, collect all unique requirement machines, one for each connected + // component of each referenced protocol. + llvm::DenseSet machines; + + // Now visit each subordinate requirement machine pull in its rules. for (auto *proto : ProtocolsToImport) { + // This will trigger requirement signature computation for this protocol, + // if neccessary, which will cause us to re-enter into a new RuleBuilder + // instace under RuleBuilder::initWithProtocolWrittenRequirements(). if (Dump) { - llvm::dbgs() << "protocol " << proto->getName() << " {\n"; + llvm::dbgs() << "importing protocol " << proto->getName() << " {\n"; } - addPermanentProtocolRules(proto); - - auto reqs = proto->getRequirementSignature(); - for (auto req : reqs.getRequirements()) - addRequirement(req.getCanonical(), proto, /*requirementID=*/None); - for (auto alias : reqs.getTypeAliases()) - addTypeAlias(alias, proto); - - if (Dump) { - llvm::dbgs() << "}\n"; + auto *machine = Context.getRequirementMachine(proto); + if (!machines.insert(machine).second) { + // We've already seen this connected component. + continue; } + + // We grab the machine's local rules, not *all* of its rules, to avoid + // duplicates in case multiple machines share a dependency on a downstream + // protocol connected component. + auto localRules = machine->getLocalRules(); + ImportedRules.insert(ImportedRules.end(), + localRules.begin(), + localRules.end()); } } diff --git a/test/Generics/rdar79564324.swift b/test/Generics/rdar79564324.swift index 5c4bf35ae9730..760cad54ed90a 100644 --- a/test/Generics/rdar79564324.swift +++ b/test/Generics/rdar79564324.swift @@ -26,14 +26,14 @@ public func test(_ t: T) where T == T.A { // CHECK-NEXT: - [P].[P] => [P] [permanent] // CHECK-NEXT: - [P].A => [P:A] [permanent] // CHECK-NEXT: - [P:A].[P] => [P:A] +// CHECK-NEXT: - [P].[P:A] => [P:A] +// CHECK-NEXT: - [P:A].A => [P:A].[P:A] // CHECK-NEXT: - τ_0_0.[P:A] => τ_0_0 // CHECK-NEXT: - τ_0_1.[P] => τ_0_1 // CHECK-NEXT: - τ_0_1.[P:A] => τ_0_0 -// CHECK-NEXT: - [P].[P:A] => [P:A] -// CHECK-NEXT: - [P:A].A => [P:A].[P:A] // CHECK-NEXT: - τ_0_0.[P] => τ_0_0 -// CHECK-NEXT: - τ_0_1.A => τ_0_0 // CHECK-NEXT: - τ_0_0.A => τ_0_0 +// CHECK-NEXT: - τ_0_1.A => τ_0_0 // CHECK-NEXT: } // CHECK: Property map: { // CHECK-NEXT: [P] => { conforms_to: [P] } diff --git a/test/Generics/rdar90219229.swift b/test/Generics/rdar90219229.swift index 197de22f178d8..bc5d22127da51 100644 --- a/test/Generics/rdar90219229.swift +++ b/test/Generics/rdar90219229.swift @@ -26,11 +26,11 @@ protocol PBad { // FIXME: Terrible diagnostics. protocol PWorse { -// expected-error@-1 2{{circular reference}} -// expected-note@-2 4{{through reference here}} +// expected-error@-1 4{{circular reference}} +// expected-note@-2 6{{through reference here}} typealias A = C associatedtype T : Self.A -// expected-note@-1 2{{while resolving type 'Self.A'}} -// expected-note@-2 2{{through reference here}} -} \ No newline at end of file +// expected-note@-1 4{{while resolving type 'Self.A'}} +// expected-note@-2 4{{through reference here}} +} diff --git a/test/Generics/unify_superclass_types_1.swift b/test/Generics/unify_superclass_types_1.swift index 68cc3837690a1..f8bd1fb2544c7 100644 --- a/test/Generics/unify_superclass_types_1.swift +++ b/test/Generics/unify_superclass_types_1.swift @@ -17,10 +17,10 @@ extension P where Self : Derived { // CHECK-NEXT: Rewrite system: { // CHECK-NEXT: - [P].[P] => [P] [permanent] // CHECK-NEXT: - [P].[superclass: Base] => [P] +// CHECK-NEXT: - [P].[layout: _NativeClass] => [P] // CHECK-NEXT: - τ_0_0.[superclass: Derived] => τ_0_0 // CHECK-NEXT: - τ_0_0.[P] => τ_0_0 // CHECK-NEXT: - τ_0_0.[superclass: Base] => τ_0_0 -// CHECK-NEXT: - [P].[layout: _NativeClass] => [P] // CHECK-NEXT: - τ_0_0.[layout: _NativeClass] => τ_0_0 // CHECK-NEXT: } // CHECK: Property map: { diff --git a/test/Generics/unify_superclass_types_2.swift b/test/Generics/unify_superclass_types_2.swift index 6be1a85bd0ade..3e9e906938e5c 100644 --- a/test/Generics/unify_superclass_types_2.swift +++ b/test/Generics/unify_superclass_types_2.swift @@ -40,8 +40,8 @@ func unifySuperclassTest(_: T) { // CHECK: } // CHECK: Property map: { // CHECK-NEXT: [P1] => { conforms_to: [P1] } -// CHECK-NEXT: [P2] => { conforms_to: [P2] } // CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Generic] } +// CHECK-NEXT: [P2] => { conforms_to: [P2] } // CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] } // CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] } // CHECK-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Generic] } diff --git a/test/Generics/unify_superclass_types_3.swift b/test/Generics/unify_superclass_types_3.swift index 02c6a7b9bff25..c09a7b62636bf 100644 --- a/test/Generics/unify_superclass_types_3.swift +++ b/test/Generics/unify_superclass_types_3.swift @@ -32,9 +32,9 @@ func unifySuperclassTest(_: T) { // CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> // CHECK-NEXT: Rewrite system: { -// CHECK: - τ_0_0.[P2:X] => τ_0_0.[P1:X] // CHECK: - [P1:X].[layout: _NativeClass] => [P1:X] // CHECK: - [P2:X].[layout: _NativeClass] => [P2:X] +// CHECK: - τ_0_0.[P2:X] => τ_0_0.[P1:X] // CHECK: - τ_0_0.[P1:X].[superclass: Generic] => τ_0_0.[P1:X] // CHECK: - τ_0_0.[P1:X].[superclass: Generic] => τ_0_0.[P1:X] // CHECK: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2] @@ -44,8 +44,8 @@ func unifySuperclassTest(_: T) { // CHECK: } // CHECK: Property map: { // CHECK-NEXT: [P1] => { conforms_to: [P1] } -// CHECK-NEXT: [P2] => { conforms_to: [P2] } // CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Derived<[P1:A1], [P1:B1]>] } +// CHECK-NEXT: [P2] => { conforms_to: [P2] } // CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] } // CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] } // CHECK-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] } diff --git a/test/Generics/unify_superclass_types_4.swift b/test/Generics/unify_superclass_types_4.swift index dfdcbf685d6d8..451439d52eb21 100644 --- a/test/Generics/unify_superclass_types_4.swift +++ b/test/Generics/unify_superclass_types_4.swift @@ -36,21 +36,21 @@ func unifySuperclassTest(_: T) { // CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> // CHECK-NEXT: Rewrite system: { // CHECK: - [P1:X].[superclass: Base<[P1:A1]>] => [P1:X] [explicit] +// CHECK: - [P1:X].[layout: _NativeClass] => [P1:X] // CHECK: - [P2:X].[superclass: Derived<[P2:A2]>] => [P2:X] [explicit] +// CHECK: - [P2:X].[layout: _NativeClass] => [P2:X] // CHECK: - τ_0_0.[P2:X] => τ_0_0.[P1:X] // CHECK: - τ_0_0.[P1:X].[superclass: Derived<τ_0_0.[P2:A2]>] => τ_0_0.[P1:X] -// CHECK: - [P1:X].[layout: _NativeClass] => [P1:X] -// CHECK: - [P2:X].[layout: _NativeClass] => [P2:X] // CHECK: - τ_0_0.[P1:X].[superclass: Base<τ_0_0.[P2:A2].[Q:T]>] => τ_0_0.[P1:X] // CHECK: - τ_0_0.[P2:A2].[Q:T] => τ_0_0.[P1:A1] // CHECK-NEXT: } // CHECK: Property map: { // CHECK-NEXT: [P1] => { conforms_to: [P1] } -// CHECK-NEXT: [P2] => { conforms_to: [P2] } -// CHECK-NEXT: [Q] => { conforms_to: [Q] } // CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Base<[P1:A1]>] } +// CHECK-NEXT: [P2] => { conforms_to: [P2] } // CHECK-NEXT: [P2:A2] => { conforms_to: [Q] } // CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Derived<[P2:A2]>] } +// CHECK-NEXT: [Q] => { conforms_to: [Q] } // CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] } // CHECK-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0.[P2:A2]>] } // CHECK-NEXT: } From d7f84454c4bafc0d96380447e0f64ed4fcee8acd Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Tue, 15 Mar 2022 23:46:18 -0400 Subject: [PATCH 121/242] RequirementMachine: Filter duplicates in ProtocolDependenciesRequest --- lib/AST/RequirementMachine/RequirementLowering.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 4058ddcf779a1..9d7ea56a8ed83 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -33,6 +33,7 @@ #include "swift/AST/TypeMatcher.h" #include "swift/AST/TypeRepr.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SetVector.h" #include "RequirementMachine.h" #include "RewriteContext.h" #include "RewriteSystem.h" @@ -1014,7 +1015,7 @@ ArrayRef ProtocolDependenciesRequest::evaluate(Evaluator &evaluator, ProtocolDecl *proto) const { auto &ctx = proto->getASTContext(); - SmallVector result; + SmallSetVector result; // If we have a serialized requirement signature, deserialize it and // look at conformance requirements. @@ -1026,7 +1027,7 @@ ProtocolDependenciesRequest::evaluate(Evaluator &evaluator, == RequirementMachineMode::Disabled)) { for (auto req : proto->getRequirementSignature().getRequirements()) { if (req.getKind() == RequirementKind::Conformance) { - result.push_back(req.getProtocolDecl()); + result.insert(req.getProtocolDecl()); } } @@ -1038,7 +1039,7 @@ ProtocolDependenciesRequest::evaluate(Evaluator &evaluator, // signature. Look at the structural requirements instead. for (auto req : proto->getStructuralRequirements()) { if (req.req.getKind() == RequirementKind::Conformance) - result.push_back(req.req.getProtocolDecl()); + result.insert(req.req.getProtocolDecl()); } return ctx.AllocateCopy(result); From c09df4910d22d8d22ea591839bdc120995e5d0c9 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Tue, 8 Mar 2022 23:48:59 -0800 Subject: [PATCH 122/242] Tests: Add integration tests for @_backDeploy that verify that use of declarations annotated with @_backDeploy behave as expected when running a client binary on an older OS that does not have the back deployed APIs. APIs with a variety of function signatures (inout parameters, throwing, generic, existential parameters and return values, etc.) are exercised to verify SILGen for these cases. --- test/attr/Inputs/BackDeployHelper.swift | 195 ++++++++++++++++++++++ test/attr/attr_backDeploy_evolution.swift | 190 +++++++++++++++++++++ 2 files changed, 385 insertions(+) create mode 100644 test/attr/Inputs/BackDeployHelper.swift create mode 100644 test/attr/attr_backDeploy_evolution.swift diff --git a/test/attr/Inputs/BackDeployHelper.swift b/test/attr/Inputs/BackDeployHelper.swift new file mode 100644 index 0000000000000..022d2368aa0eb --- /dev/null +++ b/test/attr/Inputs/BackDeployHelper.swift @@ -0,0 +1,195 @@ + +/// Returns the dsohandle for the dynamic library. +public func libraryHandle() -> UnsafeRawPointer { + return #dsohandle +} + +/// Prepends "client:" or "library:" to the given string (depending on whether +/// the dsohandle belongs to the library or not) and then prints it. +public func testPrint(handle: UnsafeRawPointer, _ string: String) { + let libraryHandle = #dsohandle + let prefix = (handle == libraryHandle) ? "library" : "client" + print("\(prefix): \(string)") +} + +/// Returns true if the host OS version is BackDeploy 2.0 or later, indicating +/// that APIs back deployed before 2.0 should run in the library. +public func isV2OrLater() -> Bool { + if #available(BackDeploy 2.0, *) { + return true + } else { + return false + } +} + +/// Returns true if BackDeploy 2.0 APIs have been stripped from the binary. +public func v2APIsAreStripped() -> Bool { +#if STRIP_V2_APIS + return true +#else + return false +#endif // STRIP_V2_APIS +} + +public enum BadError: Error, Equatable { + /// Indicates badness + case bad +} + +/// A totally unnecessary wrapper for `Int` that adds mutability. +public struct MutableInt { + @usableFromInline + internal var _value: Int + + public init(_ value: Int) { _value = value } +} + +/// A totally unnecessary wrapper for `Int` that provides reference semantics. +public class ReferenceInt { + @usableFromInline + internal var _value: Int + + public init(_ value: Int) { _value = value } +} + +/// Describes types that can be incremented. +public protocol Incrementable { + associatedtype Operand + + mutating func incrementByOne() -> String + mutating func increment(by amount: Operand) -> Operand +} + +extension MutableInt: Incrementable { + public mutating func incrementByOne() -> String { + _value += 1 + return String(_value) + } + + public mutating func increment(by amount: Int) -> Int { + _value += amount + return _value + } +} + +extension ReferenceInt: Incrementable { + public func incrementByOne() -> String { + _value += 1 + return String(_value) + } + + public func increment(by amount: Int) -> Int { + _value += amount + return _value + } +} + +extension Int { + @usableFromInline internal func byte(at index: Int) -> UInt8 { + UInt8(truncatingIfNeeded: self >> (index * 8)) + } +} + +// MARK: - Back deployed APIs + +#if !STRIP_V2_APIS + +@available(BackDeploy 1.0, *) +@_backDeploy(BackDeploy 2.0) +public func trivial() { + testPrint(handle: #dsohandle, "trivial") +} + +@available(BackDeploy 1.0, *) +@_backDeploy(BackDeploy 2.0) +public func pleaseThrow(_ shouldThrow: Bool) throws -> Bool { + if shouldThrow { throw BadError.bad } + return !shouldThrow +} + +@available(BackDeploy 1.0, *) +@_backDeploy(BackDeploy 2.0) +public func genericIncrement( + _ x: inout T, + by amount: T.Operand +) -> T.Operand { + return x.increment(by: amount) +} + +@available(BackDeploy 1.0, *) +@_backDeploy(BackDeploy 2.0) +public func existentialIncrementByOne(_ x: inout any Incrementable) { + testPrint(handle: #dsohandle, x.incrementByOne()) +} + +extension MutableInt { + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public var value: Int { _value } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public func print() { + // Tests recursive @_backDeploy since `value` is also @_backDeploy + testPrint(handle: #dsohandle, String(value)) + } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public static var zero: Self { MutableInt(0) } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public mutating func decrement(by amount: Int) -> Int { + _value -= amount + return _value + } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public func toIncrementable() -> any Incrementable { self } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public subscript(byteAt index: Int) -> UInt8 { _value.byte(at: index) } +} + +extension ReferenceInt { + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public final var value: Int { _value } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public final func print() { + // Tests recursive use of back deployed APIs, since `value` is also + testPrint(handle: #dsohandle, String(value)) + } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public final func copy() -> ReferenceInt { + return ReferenceInt(value) + } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public final class var zero: ReferenceInt { ReferenceInt(0) } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public final func decrement(by amount: Int) -> Int { + _value -= amount + return _value + } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public final func toIncrementable() -> any Incrementable { self } + + @available(BackDeploy 1.0, *) + @_backDeploy(BackDeploy 2.0) + public final subscript(byteAt index: Int) -> UInt8 { _value.byte(at: index) } +} + +#endif // !STRIP_V2_APIS diff --git a/test/attr/attr_backDeploy_evolution.swift b/test/attr/attr_backDeploy_evolution.swift new file mode 100644 index 0000000000000..27f314af07bd1 --- /dev/null +++ b/test/attr/attr_backDeploy_evolution.swift @@ -0,0 +1,190 @@ +// +// At a high level, this test is designed to verify that use of declarations +// annotated with @_backDeploy behave as expected when running a client binary +// on an older OS that does not have the back deployed APIs. The +// BackDeployHelper framework has a number of APIs that are available in the +// OSes identified by the "BackDeploy 1.0" availability macro and are back +// deployed before OSes identified "BackDeploy 2.0". Verification is performed +// the following way: +// +// 1. Build the helper framework with both BackDeploy 1.0 defined to an +// OS version before Swift ABI stability and 2.0 defined to an OS version +// after Swift ABI stability. Note that stradling ABI stability is not +// a necessary requirement of this test; it's just convenient to use OS +// versions that correspond to existing lit substitutions. +// 2. Build the client executable with a deployment target set to the same +// OS version as BackDeploy 2.0. +// 3. Run the client executable, verifying that the copies of the functions in +// the framework are used (verified by runtime logic using #dsohandle). +// 4. Build a new copy of the helper framework, this time with BackDeploy 2.0 +// set to a distant future OS version. +// 5. Build a new copy of the client executable using the new framework and +// the deployment target set to the same OS version as BackDeploy 1.0. +// 6. Run the new executable, verifying with #dsohandle that client copies of +// the APIs are used. +// 7. Re-build the framework in place, this time stripping the definitions of +// the back deployed APIs entirely. +// 8. Re-run the unmodified executable, with the same expectations as in (6). +// However, this time we're also verifying that the executable can run even +// without the original API symbols present in the linked dylib. +// + +// REQUIRES: executable_test +// REQUIRES: VENDOR=apple + +// ---- (0) Prepare SDK +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/SDK_ABI) +// RUN: %empty-directory(%t/SDK_BD) + +// ---- (1) Build famework with BackDeploy 2.0 in the past +// RUN: mkdir -p %t/SDK_ABI/Frameworks/BackDeployHelper.framework/Modules/BackDeployHelper.swiftmodule +// RUN: %target-build-swift-dylib(%t/SDK_ABI/Frameworks/BackDeployHelper.framework/BackDeployHelper) \ +// RUN: -emit-module-path %t/SDK_ABI/Frameworks/BackDeployHelper.framework/Modules/BackDeployHelper.swiftmodule/%module-target-triple.swiftmodule \ +// RUN: -module-name BackDeployHelper -emit-module %S/Inputs/BackDeployHelper.swift \ +// RUN: -Xfrontend -target -Xfrontend %target-next-stable-abi-triple \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 1.0:macOS 10.14.3, iOS 12.1, tvOS 12.1, watchOS 5.1' \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 2.0:macOS 10.15, iOS 13, tvOS 13, watchOS 6' \ +// RUN: -Xlinker -install_name -Xlinker @rpath/BackDeployHelper.framework/BackDeployHelper \ +// RUN: -enable-library-evolution + +// ---- (2) Build executable +// RUN: %target-build-swift -emit-executable %s -g -o %t/test_ABI \ +// RUN: -Xfrontend -target -Xfrontend %target-pre-stable-abi-triple \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 1.0:macOS 10.14.3, iOS 12.1, tvOS 12.1, watchOS 5.1' \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 2.0:macOS 10.15, iOS 13, tvOS 13, watchOS 6' \ +// RUN: -F %t/SDK_ABI/Frameworks/ -framework BackDeployHelper \ +// RUN: %target-rpath(@executable_path/SDK_ABI/Frameworks) + +// ---- (3) Run executable +// RUN: %target-codesign %t/test_ABI +// RUN: %target-run %t/test_ABI %t/SDK_ABI/Frameworks/BackDeployHelper.framework/BackDeployHelper | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-ABI %s + +// ---- (4) Build famework with BackDeploy 2.0 in the future +// RUN: mkdir -p %t/SDK_BD/Frameworks/BackDeployHelper.framework/Modules/BackDeployHelper.swiftmodule +// RUN: %target-build-swift-dylib(%t/SDK_BD/Frameworks/BackDeployHelper.framework/BackDeployHelper) \ +// RUN: -emit-module-path %t/SDK_BD/Frameworks/BackDeployHelper.framework/Modules/BackDeployHelper.swiftmodule/%module-target-triple.swiftmodule \ +// RUN: -module-name BackDeployHelper -emit-module %S/Inputs/BackDeployHelper.swift \ +// RUN: -Xfrontend -target -Xfrontend %target-next-stable-abi-triple \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 1.0:macOS 10.14.3, iOS 12.1, tvOS 12.1, watchOS 5.1' \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 2.0:macOS 999.0, iOS 999.0, watchOS 999.0, tvOS 999.0' \ +// RUN: -Xlinker -install_name -Xlinker @rpath/BackDeployHelper.framework/BackDeployHelper \ +// RUN: -enable-library-evolution + +// ---- (5) Build executable +// RUN: %target-build-swift -emit-executable %s -g -o %t/test_BD \ +// RUN: -Xfrontend -target -Xfrontend %target-next-stable-abi-triple \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 1.0:macOS 10.14.3, iOS 12.1, tvOS 12.1, watchOS 5.1' \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 2.0:macOS 999.0, iOS 999.0, watchOS 999.0, tvOS 999.0' \ +// RUN: -F %t/SDK_BD/Frameworks/ -framework BackDeployHelper \ +// RUN: %target-rpath(@executable_path/SDK_BD/Frameworks) + +// ---- (6) Run executable +// RUN: %target-codesign %t/test_BD +// RUN: %target-run %t/test_BD %t/SDK_BD/Frameworks/BackDeployHelper.framework/BackDeployHelper | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-BD %s + +// ---- (7) Re-build famework with the back deployed APIs stripped +// RUN: mkdir -p %t/SDK_BD/Frameworks/BackDeployHelper.framework/Modules/BackDeployHelper.swiftmodule +// RUN: %target-build-swift-dylib(%t/SDK_BD/Frameworks/BackDeployHelper.framework/BackDeployHelper) \ +// RUN: -emit-module-path %t/SDK_BD/Frameworks/BackDeployHelper.framework/Modules/BackDeployHelper.swiftmodule/%module-target-triple.swiftmodule \ +// RUN: -module-name BackDeployHelper -emit-module %S/Inputs/BackDeployHelper.swift \ +// RUN: -Xfrontend -target -Xfrontend %target-next-stable-abi-triple \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 1.0:macOS 10.14.3, iOS 12.1, tvOS 12.1, watchOS 5.1' \ +// RUN: -Xfrontend -define-availability \ +// RUN: -Xfrontend 'BackDeploy 2.0:macOS 999.0, iOS 999.0, watchOS 999.0, tvOS 999.0' \ +// RUN: -Xlinker -install_name -Xlinker @rpath/BackDeployHelper.framework/BackDeployHelper \ +// RUN: -enable-library-evolution -DSTRIP_V2_APIS + +// ---- (8) Re-run executable +// RUN: %target-codesign %t/test_BD +// RUN: %target-run %t/test_BD %t/SDK_BD/Frameworks/BackDeployHelper.framework/BackDeployHelper | %FileCheck --check-prefix=CHECK --check-prefix=CHECK-BD %s + +import BackDeployHelper + +// CHECK: client: check +testPrint(handle: #dsohandle, "check") +// CHECK: library: check +testPrint(handle: libraryHandle(), "check") + +if isV2OrLater() { + assert(!v2APIsAreStripped()) +} + +// CHECK-ABI: library: trivial +// CHECK-BD: client: trivial +trivial() + +assert(try! pleaseThrow(false)) +do { + _ = try pleaseThrow(true) + fatalError("Should have thrown") +} catch { + assert(error as? BadError == BadError.bad) +} + +do { + let zero = MutableInt.zero + assert(zero.value == 0) + + var int = MutableInt(5) + + // CHECK-ABI: library: 5 + // CHECK-BD: client: 5 + int.print() + + assert(int.increment(by: 2) == 7) + assert(genericIncrement(&int, by: 3) == 10) + assert(int.decrement(by: 1) == 9) + + var incrementable: any Incrementable = int.toIncrementable() + + // CHECK-ABI: library: 10 + // CHECK-BD: client: 10 + existentialIncrementByOne(&incrementable) + + let int2 = MutableInt(0x7BB7914B) + for (i, expectedByte) in [0x4B, 0x91, 0xB7, 0x7B].enumerated() { + assert(int2[byteAt: i] == expectedByte) + } +} + +do { + let zero = ReferenceInt.zero + assert(zero.value == 0) + + var int = ReferenceInt(42) + + // CHECK-ABI: library: 42 + // CHECK-BD: client: 42 + int.print() + + do { + let copy = int.copy() + assert(int !== copy) + assert(copy.value == 42) + } + + assert(int.increment(by: 2) == 44) + assert(genericIncrement(&int, by: 3) == 47) + assert(int.decrement(by: 46) == 1) + + var incrementable: any Incrementable = int.toIncrementable() + + // CHECK-ABI: library: 2 + // CHECK-BD: client: 2 + existentialIncrementByOne(&incrementable) + + let int2 = MutableInt(0x08AFAB76) + for (i, expectedByte) in [0x76, 0xAB, 0xAF, 0x08].enumerated() { + assert(int2[byteAt: i] == expectedByte) + } +} From e40e0f7580aab2021e319990fb0c53c69f621960 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 15 Mar 2022 22:57:07 -0600 Subject: [PATCH 123/242] [stdlib] improve UnsafeMutableRawBufferPointer.copyMemory - The previous implementation trapped when the source buffer was empty. That behaviour is both not documented and unnecessary. If the source buffer is empty, its length is necessarily shorter or equal than the length of the destination. - The updated version simply returns when the source buffer is empty. --- stdlib/public/core/UnsafeRawBufferPointer.swift.gyb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb index 6caae6dca5c8e..c0362d1049e48 100644 --- a/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb +++ b/stdlib/public/core/UnsafeRawBufferPointer.swift.gyb @@ -438,7 +438,9 @@ extension Unsafe${Mutable}RawBufferPointer { public func copyMemory(from source: UnsafeRawBufferPointer) { _debugPrecondition(source.count <= self.count, "${Self}.copyMemory source has too many elements") - baseAddress?.copyMemory(from: source.baseAddress!, byteCount: source.count) + if let baseAddress = baseAddress, let sourceAddress = source.baseAddress { + baseAddress.copyMemory(from: sourceAddress, byteCount: source.count) + } } /// Copies from a collection of `UInt8` into this buffer's memory. From cc60db78e1064867f18b64b5a4b7fa96f47d957e Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 16 Mar 2022 10:06:20 -0600 Subject: [PATCH 124/242] [test] validate behaviour when copying from an empty buffer --- .../stdlib/UnsafeBufferPointer.swift.gyb | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb index c30b2450ef92e..d1084a57eee51 100644 --- a/validation-test/stdlib/UnsafeBufferPointer.swift.gyb +++ b/validation-test/stdlib/UnsafeBufferPointer.swift.gyb @@ -179,6 +179,30 @@ ${SelfName}TestSuite.test("nilBaseAddress") { expectEqualSequence([], emptyBuffer) } +% if IsMutable: +${SelfName}TestSuite.test("copyFromEmptyBuffer") { +% if IsRaw: + var memory: UnsafeMutableRawPointer + let empty = UnsafeRawBufferPointer(start: nil, count: 0) +% else: + var memory: UnsafeMutablePointer + let empty = UnsafeBufferPointer(start: nil, count: 0) +% end + + let count = 4 + memory = allocateFor${Raw}Buffer(count: count) + defer { deallocateFor${Raw}Buffer(memory, count: count) } + + let buffer = UnsafeMutable${Raw}BufferPointer(start: memory, count: count) + +% if IsRaw: + buffer.copyMemory(from: empty) +% else: + _ = buffer.initialize(from: empty) +% end +} +% end + ${SelfName}TestSuite.test("nonNilButEmpty") { let emptyAllocated = UnsafeMutablePointer.allocate(capacity: 0) defer { emptyAllocated.deallocate() } From 8176b61d60cf36f3775315790a15819e8d53bdca Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 09:28:53 -0400 Subject: [PATCH 125/242] RequirementMachine: Tweak debug output and add an assertion --- lib/AST/RequirementMachine/ConcreteContraction.cpp | 6 ++++++ lib/AST/RequirementMachine/ConcreteTypeWitness.cpp | 4 +++- lib/AST/RequirementMachine/RequirementLowering.cpp | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/AST/RequirementMachine/ConcreteContraction.cpp b/lib/AST/RequirementMachine/ConcreteContraction.cpp index 1011ed3cb9a15..c79f43b7de785 100644 --- a/lib/AST/RequirementMachine/ConcreteContraction.cpp +++ b/lib/AST/RequirementMachine/ConcreteContraction.cpp @@ -531,6 +531,12 @@ bool ConcreteContraction::performConcreteContraction( // Phase 2: Replace each concretely-conforming generic parameter with its // concrete type. for (auto req : requirements) { + if (Debug) { + llvm::dbgs() << "@ Original requirement: "; + req.req.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + } + // Substitute the requirement. Optional substReq = substRequirement(req.req); diff --git a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp index 58845072df9f1..81d5b629550ee 100644 --- a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp +++ b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp @@ -200,7 +200,9 @@ void PropertyMap::concretizeTypeWitnessInConformance( AssociatedTypeDecl *assocType) const { auto concreteType = concreteConformanceSymbol.getConcreteType(); auto substitutions = concreteConformanceSymbol.getSubstitutions(); - auto *proto = concreteConformanceSymbol.getProtocol(); + + auto *proto = assocType->getProtocol(); + assert(proto == concreteConformanceSymbol.getProtocol()); if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { llvm::dbgs() << "^^ " << "Looking up type witness for " diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 9d7ea56a8ed83..4c3919b660aa1 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -1416,7 +1416,7 @@ void RuleBuilder::collectRulesFromReferencedProtocols() { // if neccessary, which will cause us to re-enter into a new RuleBuilder // instace under RuleBuilder::initWithProtocolWrittenRequirements(). if (Dump) { - llvm::dbgs() << "importing protocol " << proto->getName() << " {\n"; + llvm::dbgs() << "importing protocol " << proto->getName() << "\n"; } auto *machine = Context.getRequirementMachine(proto); From c25cd24fe95f306b550d0744bca199d4cec3685d Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 10:42:45 -0400 Subject: [PATCH 126/242] RequirementMachine: RewriteSystem::initialize() takes writtenRequirements as an rvalue --- lib/AST/RequirementMachine/RewriteSystem.cpp | 4 ++-- lib/AST/RequirementMachine/RewriteSystem.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp index 68e62bd698e09..303f49c711fe7 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.cpp +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -70,7 +70,7 @@ RewriteSystem::~RewriteSystem() { /// complete rewrite system. void RewriteSystem::initialize( bool recordLoops, ArrayRef protos, - ArrayRef writtenRequirements, + std::vector &&writtenRequirements, std::vector &&importedRules, std::vector> &&permanentRules, std::vector>> @@ -80,7 +80,7 @@ void RewriteSystem::initialize( RecordLoops = recordLoops; Protos = protos; - WrittenRequirements = writtenRequirements; + WrittenRequirements = std::move(writtenRequirements); // Pre-populate our rules vector with the list of imported rules, and note // the position of the first local (not imported) rule. diff --git a/lib/AST/RequirementMachine/RewriteSystem.h b/lib/AST/RequirementMachine/RewriteSystem.h index efef422471be2..57b1a06275f4e 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.h +++ b/lib/AST/RequirementMachine/RewriteSystem.h @@ -126,7 +126,7 @@ class RewriteSystem final { void initialize(bool recordLoops, ArrayRef protos, - ArrayRef writtenRequirements, + std::vector &&writtenRequirements, std::vector &&importedRules, std::vector> &&permanentRules, std::vector>> &&requirementRules); From fb487f80e7c9204931f4217ff6c5fb3ded334777 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 10:43:25 -0400 Subject: [PATCH 127/242] RequirementMachine: Factor out RewriteSystem::addRules() from RewriteSystem::initialize() --- lib/AST/RequirementMachine/RewriteSystem.cpp | 111 ++++++++++++------- lib/AST/RequirementMachine/RewriteSystem.h | 4 + 2 files changed, 76 insertions(+), 39 deletions(-) diff --git a/lib/AST/RequirementMachine/RewriteSystem.cpp b/lib/AST/RequirementMachine/RewriteSystem.cpp index 303f49c711fe7..905462f33080c 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.cpp +++ b/lib/AST/RequirementMachine/RewriteSystem.cpp @@ -82,45 +82,9 @@ void RewriteSystem::initialize( Protos = protos; WrittenRequirements = std::move(writtenRequirements); - // Pre-populate our rules vector with the list of imported rules, and note - // the position of the first local (not imported) rule. - Rules = std::move(importedRules); - FirstLocalRule = Rules.size(); - - // Add the imported rules to the trie. - for (unsigned newRuleID : indices(Rules)) { - const auto &newRule = Rules[newRuleID]; - // Skip simplified rules. At the very least we need to skip RHS-simplified - // rules since their left hand sides might duplicate existing rules; the - // others are skipped purely as an optimization. - if (newRule.isLHSSimplified() || - newRule.isRHSSimplified() || - newRule.isSubstitutionSimplified()) - continue; - - auto oldRuleID = Trie.insert(newRule.getLHS().begin(), - newRule.getLHS().end(), - newRuleID); - if (oldRuleID) { - llvm::errs() << "Imported rules have duplicate left hand sides!\n"; - llvm::errs() << "New rule #" << newRuleID << ": " << newRule << "\n"; - const auto &oldRule = getRule(*oldRuleID); - llvm::errs() << "Old rule #" << *oldRuleID << ": " << oldRule << "\n\n"; - dump(llvm::errs()); - abort(); - } - } - - // Now add our own rules. - for (const auto &rule : permanentRules) - addPermanentRule(rule.first, rule.second); - - for (const auto &rule : requirementRules) { - auto lhs = std::get<0>(rule); - auto rhs = std::get<1>(rule); - auto requirementID = std::get<2>(rule); - addExplicitRule(lhs, rhs, requirementID); - } + addRules(std::move(importedRules), + std::move(permanentRules), + std::move(requirementRules)); } /// Reduce a term by applying all rewrite rules until fixed point. @@ -330,6 +294,75 @@ bool RewriteSystem::addExplicitRule(MutableTerm lhs, MutableTerm rhs, return added; } +/// Add a set of rules from a RuleBuilder. +/// +/// This is used when building a rewrite system in initialize() above. +/// +/// It is also used when conditional requirement inference pulls in additional +/// protocols after the fact. +void RewriteSystem::addRules( + std::vector &&importedRules, + std::vector> &&permanentRules, + std::vector>> &&requirementRules) { + unsigned ruleCount = Rules.size(); + + if (ruleCount == 0) { + // Fast path if this is called from initialization; just steal the + // underlying storage of the imported rule vector. + Rules = std::move(importedRules); + } + else { + // Otherwise, copy the imported rules in. + Rules.insert(Rules.end(), importedRules.begin(), importedRules.end()); + } + + // If this is the initial call, note the first non-imported rule so that + // we can skip over imported rules later. + if (ruleCount == 0) + FirstLocalRule = Rules.size(); + + // Add the imported rules to the trie. + for (unsigned newRuleID = ruleCount, e = Rules.size(); + newRuleID < e; ++newRuleID) { + const auto &newRule = Rules[newRuleID]; + // Skip simplified rules. At the very least we need to skip RHS-simplified + // rules since their left hand sides might duplicate existing rules; the + // others are skipped purely as an optimization. + if (newRule.isLHSSimplified() || + newRule.isRHSSimplified() || + newRule.isSubstitutionSimplified()) + continue; + + auto oldRuleID = Trie.insert(newRule.getLHS().begin(), + newRule.getLHS().end(), + newRuleID); + if (oldRuleID) { + llvm::errs() << "Imported rules have duplicate left hand sides!\n"; + llvm::errs() << "New rule #" << newRuleID << ": " << newRule << "\n"; + const auto &oldRule = getRule(*oldRuleID); + llvm::errs() << "Old rule #" << *oldRuleID << ": " << oldRule << "\n\n"; + dump(llvm::errs()); + abort(); + } + } + + // Now add our own rules. + for (const auto &rule : permanentRules) + addPermanentRule(rule.first, rule.second); + + for (const auto &rule : requirementRules) { + auto lhs = std::get<0>(rule); + auto rhs = std::get<1>(rule); + auto requirementID = std::get<2>(rule); + + // When this is called while adding conditional requirements, there + // shouldn't be any new structural requirement IDs. + assert(ruleCount == 0 || !requirementID.hasValue()); + + addExplicitRule(lhs, rhs, requirementID); + } +} + /// Delete any rules whose left hand sides can be reduced by other rules. /// /// Must be run after the completion procedure, since the deletion of diff --git a/lib/AST/RequirementMachine/RewriteSystem.h b/lib/AST/RequirementMachine/RewriteSystem.h index 57b1a06275f4e..21feb77e6d3b1 100644 --- a/lib/AST/RequirementMachine/RewriteSystem.h +++ b/lib/AST/RequirementMachine/RewriteSystem.h @@ -173,6 +173,10 @@ class RewriteSystem final { bool addExplicitRule(MutableTerm lhs, MutableTerm rhs, Optional requirementID); + void addRules(std::vector &&importedRules, + std::vector> &&permanentRules, + std::vector>> &&requirementRules); + bool simplify(MutableTerm &term, RewritePath *path=nullptr) const; Optional From 652de97cfd557fdcd9b2086740672ff8aff1f83a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 10:44:18 -0400 Subject: [PATCH 128/242] RequirementMachine: Introduce RuleBuilder::initWithConditionalRequirements() --- .../RequirementLowering.cpp | 58 ++++++++++++++++--- .../RequirementMachine/RequirementLowering.h | 5 +- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 4c3919b660aa1..57278a15afb8e 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -1067,7 +1067,7 @@ void RuleBuilder::initWithGenericSignatureRequirements( // Add rewrite rules for all top-level requirements. for (const auto &req : requirements) - addRequirement(req, /*proto=*/nullptr, /*requirementID=*/None); + addRequirement(req, /*proto=*/nullptr); } /// For building a rewrite system for a generic signature from user-written @@ -1119,7 +1119,7 @@ void RuleBuilder::initWithProtocolSignatureRequirements( auto reqs = proto->getRequirementSignature(); for (auto req : reqs.getRequirements()) - addRequirement(req.getCanonical(), proto, /*requirementID=*/None); + addRequirement(req.getCanonical(), proto); for (auto alias : reqs.getTypeAliases()) addTypeAlias(alias, proto); @@ -1160,9 +1160,8 @@ void RuleBuilder::initWithProtocolWrittenRequirements( for (auto req : proto->getStructuralRequirements()) addRequirement(req, proto); - for (auto req : proto->getTypeAliasRequirements()) - addRequirement(req.getCanonical(), proto, /*requirementID=*/None); + addRequirement(req.getCanonical(), proto); for (auto *otherProto : proto->getProtocolDependencies()) addReferencedProtocol(otherProto); @@ -1177,6 +1176,43 @@ void RuleBuilder::initWithProtocolWrittenRequirements( collectRulesFromReferencedProtocols(); } +/// For adding conditional conformance requirements to an existing rewrite +/// system. This might pull in additional protocols that we haven't seen +/// before. +/// +/// The interface types in the requirements are converted to terms relative +/// to the given array of substitutions, using +/// RewriteContext::getRelativeTermForType(). +/// +/// For example, given a concrete conformance rule: +/// +/// X.Y.[concrete: Array : Equatable] +/// +/// The substitutions are {τ_0_0 := X.Z}, and the Array : Equatable conformance +/// has a conditional requirement 'τ_0_0 : Equatable', so the following +/// conformance rule will be added: +/// +/// X.Z.[Equatable] => X.Z +void RuleBuilder::initWithConditionalRequirements( + ArrayRef requirements, + ArrayRef substitutions) { + assert(!Initialized); + Initialized = 1; + + // Collect all protocols transitively referenced from these requirements. + for (auto req : requirements) { + if (req.getKind() == RequirementKind::Conformance) { + addReferencedProtocol(req.getProtocolDecl()); + } + } + + collectRulesFromReferencedProtocols(); + + // Add rewrite rules for all top-level requirements. + for (const auto &req : requirements) + addRequirement(req.getCanonical(), /*proto=*/nullptr, substitutions); +} + /// Add permanent rules for a protocol, consisting of: /// /// - The identity conformance rule [P].[P] => [P]. @@ -1323,8 +1359,16 @@ swift::rewriting::getRuleForRequirement(const Requirement &req, return std::make_pair(subjectTerm, constraintTerm); } +/// Convert a requirement to a rule and add it to the builder. +/// +/// The types in the requirement must be canonical. +/// +/// If \p substitutions is not None, the interface types in the requirement +/// are converted to terms relative to these substitutions, using +/// RewriteContext::getRelativeTermForType(). void RuleBuilder::addRequirement(const Requirement &req, const ProtocolDecl *proto, + Optional> substitutions, Optional requirementID) { if (Dump) { llvm::dbgs() << "+ "; @@ -1333,8 +1377,7 @@ void RuleBuilder::addRequirement(const Requirement &req, } auto rule = - getRuleForRequirement(req, proto, /*substitutions=*/None, - Context); + getRuleForRequirement(req, proto, substitutions, Context); RequirementRules.push_back( std::make_tuple(rule.first, rule.second, requirementID)); } @@ -1343,7 +1386,8 @@ void RuleBuilder::addRequirement(const StructuralRequirement &req, const ProtocolDecl *proto) { WrittenRequirements.push_back(req); unsigned requirementID = WrittenRequirements.size() - 1; - addRequirement(req.req.getCanonical(), proto, requirementID); + addRequirement(req.req.getCanonical(), proto, /*substitutions=*/None, + requirementID); } /// Lowers a protocol typealias to a rewrite rule. diff --git a/lib/AST/RequirementMachine/RequirementLowering.h b/lib/AST/RequirementMachine/RequirementLowering.h index d468d789259ac..3c51347703e8a 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.h +++ b/lib/AST/RequirementMachine/RequirementLowering.h @@ -132,6 +132,8 @@ struct RuleBuilder { void initWithWrittenRequirements(ArrayRef requirements); void initWithProtocolSignatureRequirements(ArrayRef proto); void initWithProtocolWrittenRequirements(ArrayRef proto); + void initWithConditionalRequirements(ArrayRef requirements, + ArrayRef substitutions); void addReferencedProtocol(const ProtocolDecl *proto); void collectRulesFromReferencedProtocols(); @@ -141,7 +143,8 @@ struct RuleBuilder { const ProtocolDecl *proto); void addRequirement(const Requirement &req, const ProtocolDecl *proto, - Optional requirementID); + Optional> substitutions=None, + Optional requirementID=None); void addRequirement(const StructuralRequirement &req, const ProtocolDecl *proto); void addTypeAlias(const ProtocolTypeAlias &alias, From c91c76028f2dc84d9c0165956da685e0ddaae593 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 10:44:38 -0400 Subject: [PATCH 129/242] RequirementMachine: Correctly handle importing of protocols in inferConditionalRequirements() This fixes a regression from the rule sharing implementation; I forgot to handle the imported rules in builder.ImportedRules here. This makes the usage of RuleBuilder in conditional requirement inference look like the other usages of RuleBuilder, except that the results are passed to RewriteSystem::addRules() instead of RewriteSystem::initialize(). --- .../ConcreteTypeWitness.cpp | 54 +++++-------------- .../conditional_requirement_inference.swift | 16 ++++-- 2 files changed, 25 insertions(+), 45 deletions(-) diff --git a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp index 81d5b629550ee..9ef5d3d77f372 100644 --- a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp +++ b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp @@ -531,8 +531,8 @@ void PropertyMap::inferConditionalRequirements( return; SmallVector desugaredRequirements; - // FIXME: Store errors in the rewrite system to be diagnosed - // from the top-level generic signature requests. + + // FIXME: Do we need to diagnose these errors? SmallVector errors; // First, desugar all conditional requirements. @@ -547,47 +547,17 @@ void PropertyMap::inferConditionalRequirements( } // Now, convert desugared conditional requirements to rules. - for (auto req : desugaredRequirements) { - if (Debug.contains(DebugFlags::ConditionalRequirements)) { - llvm::dbgs() << "@@@ Desugared requirement: "; - req.dump(llvm::dbgs()); - llvm::dbgs() << "\n"; - } - if (req.getKind() == RequirementKind::Conformance) { - auto *proto = req.getProtocolDecl(); + // This will update System.getReferencedProtocols() with any new + // protocols that were imported. + RuleBuilder builder(Context, System.getReferencedProtocols()); + builder.initWithConditionalRequirements(desugaredRequirements, + substitutions); - // If we haven't seen this protocol before, add rules for its - // requirements. - if (!System.isKnownProtocol(proto)) { - if (Debug.contains(DebugFlags::ConditionalRequirements)) { - llvm::dbgs() << "@@@ Unknown protocol: "<< proto->getName() << "\n"; - } - - RuleBuilder builder(Context, System.getReferencedProtocols()); - builder.addReferencedProtocol(proto); - builder.collectRulesFromReferencedProtocols(); + assert(builder.PermanentRules.empty()); + assert(builder.WrittenRequirements.empty()); - for (const auto &rule : builder.PermanentRules) - System.addPermanentRule(rule.first, rule.second); - - for (const auto &rule : builder.RequirementRules) { - auto lhs = std::get<0>(rule); - auto rhs = std::get<1>(rule); - System.addExplicitRule(lhs, rhs, /*requirementID=*/None); - } - } - } - - auto pair = getRuleForRequirement(req.getCanonical(), /*proto=*/nullptr, - substitutions, Context); - - if (Debug.contains(DebugFlags::ConditionalRequirements)) { - llvm::dbgs() << "@@@ Induced rule from conditional requirement: " - << pair.first << " => " << pair.second << "\n"; - } - - // FIXME: Do we need a rewrite path here? - (void) System.addRule(pair.first, pair.second); - } + System.addRules(std::move(builder.ImportedRules), + std::move(builder.PermanentRules), + std::move(builder.RequirementRules)); } diff --git a/test/Generics/conditional_requirement_inference.swift b/test/Generics/conditional_requirement_inference.swift index d95854b193a3c..3c71c8d8ffc9d 100644 --- a/test/Generics/conditional_requirement_inference.swift +++ b/test/Generics/conditional_requirement_inference.swift @@ -16,11 +16,21 @@ struct EquatableBox { // A conditional requirement with a protocol we haven't seen before. protocol First {} -protocol Second {} +protocol Second { + associatedtype X + associatedtype Y where X == Y +} extension Array : First where Element : Second {} +func sameType(_: T.Type, _: T.Type) {} + struct SillyBox { - // CHECK: Generic signature: , U : Second> - func withArray(_: U) where T == Array {} + // Make sure we pick up the same-type requirement from 'Second' -- U.Y should + // minimize down to U.X. + + // CHECK: Generic signature: , U : Second, V == U.[Second]X> + func withArray(_: U) where T == Array, V == U.Y { + sameType(U.X.self, U.Y.self) + } } \ No newline at end of file From 7c006190dd32b436529313d98f8b5f83a8dbbb82 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 10:52:45 -0400 Subject: [PATCH 130/242] RequirementMachine: Fold getRuleForRequirement() into RuleBuilder::addRequirement() --- .../RequirementLowering.cpp | 80 +++++++++---------- .../RequirementMachine/RequirementLowering.h | 6 -- 2 files changed, 36 insertions(+), 50 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 57278a15afb8e..aa854827780d3 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -1258,26 +1258,39 @@ void RuleBuilder::addAssociatedType(const AssociatedTypeDecl *type, /// Lowers a desugared generic requirement to a rewrite rule. /// -/// If \p proto is null, this is a generic requirement from the top-level -/// generic signature. The added rewrite rule will be rooted in a generic -/// parameter symbol. +/// Convert a requirement to a rule and add it to the builder. +/// +/// The types in the requirement must be canonical. +/// +/// If \p proto is null and \p substitutions is None, this is a generic +/// requirement from the top-level generic signature. The added rewrite +/// rule will be rooted in a generic parameter symbol. /// /// If \p proto is non-null, this is a generic requirement in the protocol's /// requirement signature. The added rewrite rule will be rooted in a /// protocol symbol. -std::pair -swift::rewriting::getRuleForRequirement(const Requirement &req, - const ProtocolDecl *proto, - Optional> substitutions, - RewriteContext &ctx) { +/// +/// If \p substitutions is not None, this is a conditional requirement +/// added by conditional requirement inference. The added rewrite rule +/// will be added in the corresponding term from the substitution array. +void RuleBuilder::addRequirement(const Requirement &req, + const ProtocolDecl *proto, + Optional> substitutions, + Optional requirementID) { + if (Dump) { + llvm::dbgs() << "+ "; + req.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + } + assert(!substitutions.hasValue() || proto == nullptr && "Can't have both"); // Compute the left hand side. auto subjectType = CanType(req.getFirstType()); auto subjectTerm = (substitutions - ? ctx.getRelativeTermForType( + ? Context.getRelativeTermForType( subjectType, *substitutions) - : ctx.getMutableTermForType( + : Context.getMutableTermForType( subjectType, proto)); // Compute the right hand side. @@ -1293,7 +1306,7 @@ swift::rewriting::getRuleForRequirement(const Requirement &req, auto *proto = req.getProtocolDecl(); constraintTerm = subjectTerm; - constraintTerm.add(Symbol::forProtocol(proto, ctx)); + constraintTerm.add(Symbol::forProtocol(proto, Context)); break; } @@ -1306,11 +1319,11 @@ swift::rewriting::getRuleForRequirement(const Requirement &req, // Build the symbol [superclass: C]. SmallVector result; otherType = (substitutions - ? ctx.getRelativeSubstitutionSchemaFromType( + ? Context.getRelativeSubstitutionSchemaFromType( otherType, *substitutions, result) - : ctx.getSubstitutionSchemaFromType( + : Context.getSubstitutionSchemaFromType( otherType, proto, result)); - auto superclassSymbol = Symbol::forSuperclass(otherType, result, ctx); + auto superclassSymbol = Symbol::forSuperclass(otherType, result, Context); // Build the term T.[superclass: C]. constraintTerm = subjectTerm; @@ -1323,7 +1336,7 @@ swift::rewriting::getRuleForRequirement(const Requirement &req, // // T.[layout: L] == T constraintTerm = subjectTerm; - constraintTerm.add(Symbol::forLayout(req.getLayoutConstraint(), ctx)); + constraintTerm.add(Symbol::forLayout(req.getLayoutConstraint(), Context)); break; } @@ -1337,49 +1350,28 @@ swift::rewriting::getRuleForRequirement(const Requirement &req, // T.[concrete: C] => T SmallVector result; otherType = (substitutions - ? ctx.getRelativeSubstitutionSchemaFromType( + ? Context.getRelativeSubstitutionSchemaFromType( otherType, *substitutions, result) - : ctx.getSubstitutionSchemaFromType( + : Context.getSubstitutionSchemaFromType( otherType, proto, result)); constraintTerm = subjectTerm; - constraintTerm.add(Symbol::forConcreteType(otherType, result, ctx)); + constraintTerm.add(Symbol::forConcreteType(otherType, result, Context)); break; } constraintTerm = (substitutions - ? ctx.getRelativeTermForType( + ? Context.getRelativeTermForType( otherType, *substitutions) - : ctx.getMutableTermForType( + : Context.getMutableTermForType( otherType, proto)); break; } } - return std::make_pair(subjectTerm, constraintTerm); -} - -/// Convert a requirement to a rule and add it to the builder. -/// -/// The types in the requirement must be canonical. -/// -/// If \p substitutions is not None, the interface types in the requirement -/// are converted to terms relative to these substitutions, using -/// RewriteContext::getRelativeTermForType(). -void RuleBuilder::addRequirement(const Requirement &req, - const ProtocolDecl *proto, - Optional> substitutions, - Optional requirementID) { - if (Dump) { - llvm::dbgs() << "+ "; - req.dump(llvm::dbgs()); - llvm::dbgs() << "\n"; - } - - auto rule = - getRuleForRequirement(req, proto, substitutions, Context); - RequirementRules.push_back( - std::make_tuple(rule.first, rule.second, requirementID)); + RequirementRules.emplace_back( + std::move(subjectTerm), std::move(constraintTerm), + requirementID); } void RuleBuilder::addRequirement(const StructuralRequirement &req, diff --git a/lib/AST/RequirementMachine/RequirementLowering.h b/lib/AST/RequirementMachine/RequirementLowering.h index 3c51347703e8a..7d8fce6dbc60c 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.h +++ b/lib/AST/RequirementMachine/RequirementLowering.h @@ -63,12 +63,6 @@ bool diagnoseRequirementErrors(ASTContext &ctx, ArrayRef errors, bool allowConcreteGenericParams); -std::pair -getRuleForRequirement(const Requirement &req, - const ProtocolDecl *proto, - Optional> substitutions, - RewriteContext &ctx); - /// A utility class for bulding rewrite rules from the top-level requirements /// of a generic signature. /// From ef1636a46233de60a53a85b18880cade93f3678b Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 11:01:20 -0400 Subject: [PATCH 131/242] RequirementMachine: Split off RuleBuilder.{cpp,h} from RequirementLowering.{cpp,h} --- lib/AST/CMakeLists.txt | 1 + .../ConcreteTypeWitness.cpp | 1 + .../RequirementLowering.cpp | 456 +----------------- .../RequirementMachine/RequirementLowering.h | 90 +--- .../RequirementMachine/RequirementMachine.cpp | 1 + lib/AST/RequirementMachine/RuleBuilder.cpp | 455 +++++++++++++++++ lib/AST/RequirementMachine/RuleBuilder.h | 125 +++++ 7 files changed, 590 insertions(+), 539 deletions(-) create mode 100644 lib/AST/RequirementMachine/RuleBuilder.cpp create mode 100644 lib/AST/RequirementMachine/RuleBuilder.h diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index d56782623394c..dc334dc2eb148 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -95,6 +95,7 @@ add_swift_host_library(swiftAST STATIC RequirementMachine/RewriteLoop.cpp RequirementMachine/RewriteSystem.cpp RequirementMachine/Rule.cpp + RequirementMachine/RuleBuilder.cpp RequirementMachine/SimplifySubstitutions.cpp RequirementMachine/Symbol.cpp RequirementMachine/Term.cpp diff --git a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp index 9ef5d3d77f372..9b43018374a7e 100644 --- a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp +++ b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp @@ -24,6 +24,7 @@ #include #include "PropertyMap.h" #include "RequirementLowering.h" +#include "RuleBuilder.h" using namespace swift; using namespace rewriting; diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index aa854827780d3..417c1751a7f7b 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -1,4 +1,4 @@ -//===--- RequirementLowering.cpp - Building rules from requirements -------===// +//===--- RequirementLowering.cpp - Requirement inference and desugaring ---===// // // This source file is part of the Swift.org open source project // @@ -10,15 +10,10 @@ // //===----------------------------------------------------------------------===// // -// This file implements logic for lowering generic requirements to rewrite rules -// in the requirement machine. -// -// This includes generic requirements from canonical generic signatures and -// protocol requirement signatures, as well as user-written requirements in -// protocols ("structural requirements") and the 'where' clauses of generic -// declarations. -// -// There is some additional desugaring logic for user-written requirements. +// This file implements logic for desugaring requirements, for example breaking +// down 'T : P & Q' into 'T : P' and 'T : Q', as well as requirement inference, +// where an occurrence of 'Set' in a function signature introduces the +// requirement 'T : Hashable' from the generic signature of 'struct Set'. // //===----------------------------------------------------------------------===// @@ -26,7 +21,6 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/DiagnosticsSema.h" -#include "swift/AST/ExistentialLayout.h" #include "swift/AST/Requirement.h" #include "swift/AST/RequirementSignature.h" #include "swift/AST/TypeCheckRequests.h" @@ -34,25 +28,11 @@ #include "swift/AST/TypeRepr.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SetVector.h" -#include "RequirementMachine.h" #include "RewriteContext.h" -#include "RewriteSystem.h" -#include "Symbol.h" -#include "Term.h" using namespace swift; using namespace rewriting; -// -// Requirement desugaring -- used in two places: -// -// 1) AbstractGenericSignatureRequest, where the added requirements might have -// substitutions applied. -// -// 2) StructuralRequirementsRequest, which performs further processing to wrap -// desugared requirements with source location information. -// - /// Desugar a same-type requirement that possibly has concrete types on either /// side into a series of same-type and concrete-type requirements where the /// left hand side is always a type parameter. @@ -1044,429 +1024,3 @@ ProtocolDependenciesRequest::evaluate(Evaluator &evaluator, return ctx.AllocateCopy(result); } - -// -// Building rewrite rules from desugared requirements. -// - -/// For building a rewrite system for a generic signature from canonical -/// requirements. -void RuleBuilder::initWithGenericSignatureRequirements( - ArrayRef requirements) { - assert(!Initialized); - Initialized = 1; - - // Collect all protocols transitively referenced from these requirements. - for (auto req : requirements) { - if (req.getKind() == RequirementKind::Conformance) { - addReferencedProtocol(req.getProtocolDecl()); - } - } - - collectRulesFromReferencedProtocols(); - - // Add rewrite rules for all top-level requirements. - for (const auto &req : requirements) - addRequirement(req, /*proto=*/nullptr); -} - -/// For building a rewrite system for a generic signature from user-written -/// requirements. -void RuleBuilder::initWithWrittenRequirements( - ArrayRef requirements) { - assert(!Initialized); - Initialized = 1; - - // Collect all protocols transitively referenced from these requirements. - for (auto req : requirements) { - if (req.req.getKind() == RequirementKind::Conformance) { - addReferencedProtocol(req.req.getProtocolDecl()); - } - } - - collectRulesFromReferencedProtocols(); - - // Add rewrite rules for all top-level requirements. - for (const auto &req : requirements) - addRequirement(req, /*proto=*/nullptr); -} - -/// For building a rewrite system for a protocol connected component from -/// a previously-built requirement signature. -/// -/// Will trigger requirement signature computation if we haven't built -/// requirement signatures for this connected component yet, in which case we -/// will recursively end up building another rewrite system for this component -/// using initWithProtocolWrittenRequirements(). -void RuleBuilder::initWithProtocolSignatureRequirements( - ArrayRef protos) { - assert(!Initialized); - Initialized = 1; - - // Add all protocols to the referenced set, so that subsequent calls - // to addReferencedProtocol() with one of these protocols don't add - // them to the import list. - for (auto *proto : protos) { - ReferencedProtocols.insert(proto); - } - - for (auto *proto : protos) { - if (Dump) { - llvm::dbgs() << "protocol " << proto->getName() << " {\n"; - } - - addPermanentProtocolRules(proto); - - auto reqs = proto->getRequirementSignature(); - for (auto req : reqs.getRequirements()) - addRequirement(req.getCanonical(), proto); - for (auto alias : reqs.getTypeAliases()) - addTypeAlias(alias, proto); - - for (auto *otherProto : proto->getProtocolDependencies()) - addReferencedProtocol(otherProto); - - if (Dump) { - llvm::dbgs() << "}\n"; - } - } - - // Collect all protocols transitively referenced from this connected component - // of the protocol dependency graph. - collectRulesFromReferencedProtocols(); -} - -/// For building a rewrite system for a protocol connected component from -/// user-written requirements. Used when actually building requirement -/// signatures. -void RuleBuilder::initWithProtocolWrittenRequirements( - ArrayRef protos) { - assert(!Initialized); - Initialized = 1; - - // Add all protocols to the referenced set, so that subsequent calls - // to addReferencedProtocol() with one of these protocols don't add - // them to the import list. - for (auto *proto : protos) { - ReferencedProtocols.insert(proto); - } - - for (auto *proto : protos) { - if (Dump) { - llvm::dbgs() << "protocol " << proto->getName() << " {\n"; - } - - addPermanentProtocolRules(proto); - - for (auto req : proto->getStructuralRequirements()) - addRequirement(req, proto); - for (auto req : proto->getTypeAliasRequirements()) - addRequirement(req.getCanonical(), proto); - - for (auto *otherProto : proto->getProtocolDependencies()) - addReferencedProtocol(otherProto); - - if (Dump) { - llvm::dbgs() << "}\n"; - } - } - - // Collect all protocols transitively referenced from this connected component - // of the protocol dependency graph. - collectRulesFromReferencedProtocols(); -} - -/// For adding conditional conformance requirements to an existing rewrite -/// system. This might pull in additional protocols that we haven't seen -/// before. -/// -/// The interface types in the requirements are converted to terms relative -/// to the given array of substitutions, using -/// RewriteContext::getRelativeTermForType(). -/// -/// For example, given a concrete conformance rule: -/// -/// X.Y.[concrete: Array : Equatable] -/// -/// The substitutions are {τ_0_0 := X.Z}, and the Array : Equatable conformance -/// has a conditional requirement 'τ_0_0 : Equatable', so the following -/// conformance rule will be added: -/// -/// X.Z.[Equatable] => X.Z -void RuleBuilder::initWithConditionalRequirements( - ArrayRef requirements, - ArrayRef substitutions) { - assert(!Initialized); - Initialized = 1; - - // Collect all protocols transitively referenced from these requirements. - for (auto req : requirements) { - if (req.getKind() == RequirementKind::Conformance) { - addReferencedProtocol(req.getProtocolDecl()); - } - } - - collectRulesFromReferencedProtocols(); - - // Add rewrite rules for all top-level requirements. - for (const auto &req : requirements) - addRequirement(req.getCanonical(), /*proto=*/nullptr, substitutions); -} - -/// Add permanent rules for a protocol, consisting of: -/// -/// - The identity conformance rule [P].[P] => [P]. -/// - An associated type introduction rule for each associated type. -/// - An inherited associated type introduction rule for each associated -/// type of each inherited protocol. -void RuleBuilder::addPermanentProtocolRules(const ProtocolDecl *proto) { - MutableTerm lhs; - lhs.add(Symbol::forProtocol(proto, Context)); - lhs.add(Symbol::forProtocol(proto, Context)); - - MutableTerm rhs; - rhs.add(Symbol::forProtocol(proto, Context)); - - PermanentRules.emplace_back(lhs, rhs); - - for (auto *assocType : proto->getAssociatedTypeMembers()) - addAssociatedType(assocType, proto); - - for (auto *inheritedProto : Context.getInheritedProtocols(proto)) { - for (auto *assocType : inheritedProto->getAssociatedTypeMembers()) - addAssociatedType(assocType, proto); - } -} - -/// For an associated type T in a protocol P, we add a rewrite rule: -/// -/// [P].T => [P:T] -/// -/// Intuitively, this means "if a type conforms to P, it has a nested type -/// named T". -void RuleBuilder::addAssociatedType(const AssociatedTypeDecl *type, - const ProtocolDecl *proto) { - MutableTerm lhs; - lhs.add(Symbol::forProtocol(proto, Context)); - lhs.add(Symbol::forName(type->getName(), Context)); - - MutableTerm rhs; - rhs.add(Symbol::forAssociatedType(proto, type->getName(), Context)); - - PermanentRules.emplace_back(lhs, rhs); -} - -/// Lowers a desugared generic requirement to a rewrite rule. -/// -/// Convert a requirement to a rule and add it to the builder. -/// -/// The types in the requirement must be canonical. -/// -/// If \p proto is null and \p substitutions is None, this is a generic -/// requirement from the top-level generic signature. The added rewrite -/// rule will be rooted in a generic parameter symbol. -/// -/// If \p proto is non-null, this is a generic requirement in the protocol's -/// requirement signature. The added rewrite rule will be rooted in a -/// protocol symbol. -/// -/// If \p substitutions is not None, this is a conditional requirement -/// added by conditional requirement inference. The added rewrite rule -/// will be added in the corresponding term from the substitution array. -void RuleBuilder::addRequirement(const Requirement &req, - const ProtocolDecl *proto, - Optional> substitutions, - Optional requirementID) { - if (Dump) { - llvm::dbgs() << "+ "; - req.dump(llvm::dbgs()); - llvm::dbgs() << "\n"; - } - - assert(!substitutions.hasValue() || proto == nullptr && "Can't have both"); - - // Compute the left hand side. - auto subjectType = CanType(req.getFirstType()); - auto subjectTerm = (substitutions - ? Context.getRelativeTermForType( - subjectType, *substitutions) - : Context.getMutableTermForType( - subjectType, proto)); - - // Compute the right hand side. - MutableTerm constraintTerm; - - switch (req.getKind()) { - case RequirementKind::Conformance: { - // A conformance requirement T : P becomes a rewrite rule - // - // T.[P] == T - // - // Intuitively, this means "any type ending with T conforms to P". - auto *proto = req.getProtocolDecl(); - - constraintTerm = subjectTerm; - constraintTerm.add(Symbol::forProtocol(proto, Context)); - break; - } - - case RequirementKind::Superclass: { - // A superclass requirement T : C becomes a rewrite rule - // - // T.[superclass: C] => T - auto otherType = CanType(req.getSecondType()); - - // Build the symbol [superclass: C]. - SmallVector result; - otherType = (substitutions - ? Context.getRelativeSubstitutionSchemaFromType( - otherType, *substitutions, result) - : Context.getSubstitutionSchemaFromType( - otherType, proto, result)); - auto superclassSymbol = Symbol::forSuperclass(otherType, result, Context); - - // Build the term T.[superclass: C]. - constraintTerm = subjectTerm; - constraintTerm.add(superclassSymbol); - break; - } - - case RequirementKind::Layout: { - // A layout requirement T : L becomes a rewrite rule - // - // T.[layout: L] == T - constraintTerm = subjectTerm; - constraintTerm.add(Symbol::forLayout(req.getLayoutConstraint(), Context)); - break; - } - - case RequirementKind::SameType: { - auto otherType = CanType(req.getSecondType()); - - if (!otherType->isTypeParameter()) { - // A concrete same-type requirement T == C becomes a - // rewrite rule - // - // T.[concrete: C] => T - SmallVector result; - otherType = (substitutions - ? Context.getRelativeSubstitutionSchemaFromType( - otherType, *substitutions, result) - : Context.getSubstitutionSchemaFromType( - otherType, proto, result)); - - constraintTerm = subjectTerm; - constraintTerm.add(Symbol::forConcreteType(otherType, result, Context)); - break; - } - - constraintTerm = (substitutions - ? Context.getRelativeTermForType( - otherType, *substitutions) - : Context.getMutableTermForType( - otherType, proto)); - break; - } - } - - RequirementRules.emplace_back( - std::move(subjectTerm), std::move(constraintTerm), - requirementID); -} - -void RuleBuilder::addRequirement(const StructuralRequirement &req, - const ProtocolDecl *proto) { - WrittenRequirements.push_back(req); - unsigned requirementID = WrittenRequirements.size() - 1; - addRequirement(req.req.getCanonical(), proto, /*substitutions=*/None, - requirementID); -} - -/// Lowers a protocol typealias to a rewrite rule. -void RuleBuilder::addTypeAlias(const ProtocolTypeAlias &alias, - const ProtocolDecl *proto) { - // Build the term [P].T, where P is the protocol and T is a name symbol. - MutableTerm subjectTerm; - subjectTerm.add(Symbol::forProtocol(proto, Context)); - subjectTerm.add(Symbol::forName(alias.getName(), Context)); - - auto constraintType = alias.getUnderlyingType()->getCanonicalType(); - MutableTerm constraintTerm; - - if (constraintType->isTypeParameter()) { - // If the underlying type of the typealias is a type parameter X, build - // a rule [P].T => X, where X, - constraintTerm = Context.getMutableTermForType( - constraintType, proto); - } else { - // If the underlying type of the typealias is a concrete type C, build - // a rule [P].T.[concrete: C] => [P].T. - constraintTerm = subjectTerm; - - SmallVector result; - auto concreteType = - Context.getSubstitutionSchemaFromType( - constraintType, proto, result); - - constraintTerm.add(Symbol::forConcreteType(concreteType, result, Context)); - } - - RequirementRules.emplace_back(subjectTerm, constraintTerm, - /*requirementID=*/None); -} - -/// If we haven't seen this protocol yet, save it for later so that we can -/// import the rewrite rules from its connected component. -void RuleBuilder::addReferencedProtocol(const ProtocolDecl *proto) { - if (ReferencedProtocols.insert(proto).second) - ProtocolsToImport.push_back(proto); -} - -/// Compute the transitive closure of the set of all protocols referenced from -/// the right hand sides of conformance requirements, and convert their -/// requirements to rewrite rules. -void RuleBuilder::collectRulesFromReferencedProtocols() { - // Compute the transitive closure. - unsigned i = 0; - while (i < ProtocolsToImport.size()) { - auto *proto = ProtocolsToImport[i++]; - for (auto *depProto : proto->getProtocolDependencies()) { - addReferencedProtocol(depProto); - } - } - - // If this is a rewrite system for a generic signature, add rewrite rules for - // each referenced protocol. - // - // if this is a rewrite system for a connected component of the protocol - // dependency graph, add rewrite rules for each referenced protocol not part - // of this connected component. - - // First, collect all unique requirement machines, one for each connected - // component of each referenced protocol. - llvm::DenseSet machines; - - // Now visit each subordinate requirement machine pull in its rules. - for (auto *proto : ProtocolsToImport) { - // This will trigger requirement signature computation for this protocol, - // if neccessary, which will cause us to re-enter into a new RuleBuilder - // instace under RuleBuilder::initWithProtocolWrittenRequirements(). - if (Dump) { - llvm::dbgs() << "importing protocol " << proto->getName() << "\n"; - } - - auto *machine = Context.getRequirementMachine(proto); - if (!machines.insert(machine).second) { - // We've already seen this connected component. - continue; - } - - // We grab the machine's local rules, not *all* of its rules, to avoid - // duplicates in case multiple machines share a dependency on a downstream - // protocol connected component. - auto localRules = machine->getLocalRules(); - ImportedRules.insert(ImportedRules.end(), - localRules.begin(), - localRules.end()); - } -} diff --git a/lib/AST/RequirementMachine/RequirementLowering.h b/lib/AST/RequirementMachine/RequirementLowering.h index 7d8fce6dbc60c..ea53a07475f5e 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.h +++ b/lib/AST/RequirementMachine/RequirementLowering.h @@ -1,4 +1,4 @@ -//===--- RequirementLowering.h - Building rules from requirements ---------===// +//===--- RequirementLowering.h - Requirement inference and desugaring -----===// // // This source file is part of the Swift.org open source project // @@ -19,10 +19,6 @@ #include "llvm/ADT/SmallVector.h" #include #include "Diagnostics.h" -#include "RewriteContext.h" -#include "Rule.h" -#include "Symbol.h" -#include "Term.h" namespace llvm { class raw_ostream; @@ -38,7 +34,7 @@ class Requirement; namespace rewriting { // Entry points used by AbstractGenericSignatureRequest and -// InferredGenericSignatureRequest; see RequiremetnLowering.cpp for +// InferredGenericSignatureRequest; see RequirementLowering.cpp for // documentation // comments. @@ -63,88 +59,6 @@ bool diagnoseRequirementErrors(ASTContext &ctx, ArrayRef errors, bool allowConcreteGenericParams); -/// A utility class for bulding rewrite rules from the top-level requirements -/// of a generic signature. -/// -/// This also collects requirements from the transitive closure of all protocols -/// appearing on the right hand side of conformance requirements. -struct RuleBuilder { - RewriteContext &Context; - - /// The transitive closure of all protocols appearing on the right hand - /// side of conformance requirements. - llvm::DenseSet &ReferencedProtocols; - - /// A subset of the above in insertion order, consisting of the protocols - /// whose rules we are going to import. - /// - /// If this is a rewrite system built from a generic signature, this vector - /// contains all elements in the above set. - /// - /// If this is a rewrite system built from a strongly connected component - /// of the protocol, this vector contains all elements in the above set - /// except for the protocols belonging to the component representing the - /// rewrite system itself; those protocols are added directly instead of - /// being imported. - std::vector ProtocolsToImport; - - /// The rules representing a complete rewrite system for the above vector, - /// pulled in by collectRulesFromReferencedProtocols(). - std::vector ImportedRules; - - /// New rules to add which will be marked 'permanent'. These are rules for - /// introducing associated types, and relationships between layout, - /// superclass and concrete type symbols. They are not eliminated by - /// homotopy reduction, since they are always added when the rewrite system - /// is built. - std::vector> PermanentRules; - - /// New rules derived from requirements written by the user, which can be - /// eliminated by homotopy reduction. - std::vector>> - RequirementRules; - - /// Requirements written in source code. The requirement ID in the above - /// \c RequirementRules vector is an index into this array. - std::vector WrittenRequirements; - - /// Enables debugging output. Controlled by the -dump-requirement-machine - /// frontend flag. - unsigned Dump : 1; - - /// Used to ensure the initWith*() methods are only called once. - unsigned Initialized : 1; - - RuleBuilder(RewriteContext &ctx, - llvm::DenseSet &referencedProtocols) - : Context(ctx), ReferencedProtocols(referencedProtocols) { - Dump = ctx.getASTContext().LangOpts.DumpRequirementMachine; - Initialized = 0; - } - - void initWithGenericSignatureRequirements(ArrayRef requirements); - void initWithWrittenRequirements(ArrayRef requirements); - void initWithProtocolSignatureRequirements(ArrayRef proto); - void initWithProtocolWrittenRequirements(ArrayRef proto); - void initWithConditionalRequirements(ArrayRef requirements, - ArrayRef substitutions); - void addReferencedProtocol(const ProtocolDecl *proto); - void collectRulesFromReferencedProtocols(); - -private: - void addPermanentProtocolRules(const ProtocolDecl *proto); - void addAssociatedType(const AssociatedTypeDecl *type, - const ProtocolDecl *proto); - void addRequirement(const Requirement &req, - const ProtocolDecl *proto, - Optional> substitutions=None, - Optional requirementID=None); - void addRequirement(const StructuralRequirement &req, - const ProtocolDecl *proto); - void addTypeAlias(const ProtocolTypeAlias &alias, - const ProtocolDecl *proto); -}; - // Defined in ConcreteContraction.cpp. bool performConcreteContraction( ArrayRef requirements, diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp index 19d8bfcfd1912..b8ad293e39f3d 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.cpp +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -17,6 +17,7 @@ #include "swift/AST/PrettyStackTrace.h" #include "swift/AST/Requirement.h" #include "RequirementLowering.h" +#include "RuleBuilder.h" using namespace swift; using namespace rewriting; diff --git a/lib/AST/RequirementMachine/RuleBuilder.cpp b/lib/AST/RequirementMachine/RuleBuilder.cpp new file mode 100644 index 0000000000000..6c03267723ac9 --- /dev/null +++ b/lib/AST/RequirementMachine/RuleBuilder.cpp @@ -0,0 +1,455 @@ +//===--- RuleBuilder.cpp - Lowering desugared requirements to rules -------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file implements lowering of desugared requirements to rewrite rules, +// as well as rule sharing, which imports confluent rewrite rules from a +// protocol connected component into a rewrite system which references that +// protocol. +// +//===----------------------------------------------------------------------===// + +#include "RuleBuilder.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Requirement.h" +#include "swift/AST/RequirementSignature.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SetVector.h" +#include "RequirementMachine.h" +#include "RewriteContext.h" +#include "RewriteSystem.h" +#include "Symbol.h" +#include "Term.h" + +using namespace swift; +using namespace rewriting; + +/// For building a rewrite system for a generic signature from canonical +/// requirements. +void RuleBuilder::initWithGenericSignatureRequirements( + ArrayRef requirements) { + assert(!Initialized); + Initialized = 1; + + // Collect all protocols transitively referenced from these requirements. + for (auto req : requirements) { + if (req.getKind() == RequirementKind::Conformance) { + addReferencedProtocol(req.getProtocolDecl()); + } + } + + collectRulesFromReferencedProtocols(); + + // Add rewrite rules for all top-level requirements. + for (const auto &req : requirements) + addRequirement(req, /*proto=*/nullptr); +} + +/// For building a rewrite system for a generic signature from user-written +/// requirements. +void RuleBuilder::initWithWrittenRequirements( + ArrayRef requirements) { + assert(!Initialized); + Initialized = 1; + + // Collect all protocols transitively referenced from these requirements. + for (auto req : requirements) { + if (req.req.getKind() == RequirementKind::Conformance) { + addReferencedProtocol(req.req.getProtocolDecl()); + } + } + + collectRulesFromReferencedProtocols(); + + // Add rewrite rules for all top-level requirements. + for (const auto &req : requirements) + addRequirement(req, /*proto=*/nullptr); +} + +/// For building a rewrite system for a protocol connected component from +/// a previously-built requirement signature. +/// +/// Will trigger requirement signature computation if we haven't built +/// requirement signatures for this connected component yet, in which case we +/// will recursively end up building another rewrite system for this component +/// using initWithProtocolWrittenRequirements(). +void RuleBuilder::initWithProtocolSignatureRequirements( + ArrayRef protos) { + assert(!Initialized); + Initialized = 1; + + // Add all protocols to the referenced set, so that subsequent calls + // to addReferencedProtocol() with one of these protocols don't add + // them to the import list. + for (auto *proto : protos) { + ReferencedProtocols.insert(proto); + } + + for (auto *proto : protos) { + if (Dump) { + llvm::dbgs() << "protocol " << proto->getName() << " {\n"; + } + + addPermanentProtocolRules(proto); + + auto reqs = proto->getRequirementSignature(); + for (auto req : reqs.getRequirements()) + addRequirement(req.getCanonical(), proto); + for (auto alias : reqs.getTypeAliases()) + addTypeAlias(alias, proto); + + for (auto *otherProto : proto->getProtocolDependencies()) + addReferencedProtocol(otherProto); + + if (Dump) { + llvm::dbgs() << "}\n"; + } + } + + // Collect all protocols transitively referenced from this connected component + // of the protocol dependency graph. + collectRulesFromReferencedProtocols(); +} + +/// For building a rewrite system for a protocol connected component from +/// user-written requirements. Used when actually building requirement +/// signatures. +void RuleBuilder::initWithProtocolWrittenRequirements( + ArrayRef protos) { + assert(!Initialized); + Initialized = 1; + + // Add all protocols to the referenced set, so that subsequent calls + // to addReferencedProtocol() with one of these protocols don't add + // them to the import list. + for (auto *proto : protos) { + ReferencedProtocols.insert(proto); + } + + for (auto *proto : protos) { + if (Dump) { + llvm::dbgs() << "protocol " << proto->getName() << " {\n"; + } + + addPermanentProtocolRules(proto); + + for (auto req : proto->getStructuralRequirements()) + addRequirement(req, proto); + for (auto req : proto->getTypeAliasRequirements()) + addRequirement(req.getCanonical(), proto); + + for (auto *otherProto : proto->getProtocolDependencies()) + addReferencedProtocol(otherProto); + + if (Dump) { + llvm::dbgs() << "}\n"; + } + } + + // Collect all protocols transitively referenced from this connected component + // of the protocol dependency graph. + collectRulesFromReferencedProtocols(); +} + +/// For adding conditional conformance requirements to an existing rewrite +/// system. This might pull in additional protocols that we haven't seen +/// before. +/// +/// The interface types in the requirements are converted to terms relative +/// to the given array of substitutions, using +/// RewriteContext::getRelativeTermForType(). +/// +/// For example, given a concrete conformance rule: +/// +/// X.Y.[concrete: Array : Equatable] +/// +/// The substitutions are {τ_0_0 := X.Z}, and the Array : Equatable conformance +/// has a conditional requirement 'τ_0_0 : Equatable', so the following +/// conformance rule will be added: +/// +/// X.Z.[Equatable] => X.Z +void RuleBuilder::initWithConditionalRequirements( + ArrayRef requirements, + ArrayRef substitutions) { + assert(!Initialized); + Initialized = 1; + + // Collect all protocols transitively referenced from these requirements. + for (auto req : requirements) { + if (req.getKind() == RequirementKind::Conformance) { + addReferencedProtocol(req.getProtocolDecl()); + } + } + + collectRulesFromReferencedProtocols(); + + // Add rewrite rules for all top-level requirements. + for (const auto &req : requirements) + addRequirement(req.getCanonical(), /*proto=*/nullptr, substitutions); +} + +/// Add permanent rules for a protocol, consisting of: +/// +/// - The identity conformance rule [P].[P] => [P]. +/// - An associated type introduction rule for each associated type. +/// - An inherited associated type introduction rule for each associated +/// type of each inherited protocol. +void RuleBuilder::addPermanentProtocolRules(const ProtocolDecl *proto) { + MutableTerm lhs; + lhs.add(Symbol::forProtocol(proto, Context)); + lhs.add(Symbol::forProtocol(proto, Context)); + + MutableTerm rhs; + rhs.add(Symbol::forProtocol(proto, Context)); + + PermanentRules.emplace_back(lhs, rhs); + + for (auto *assocType : proto->getAssociatedTypeMembers()) + addAssociatedType(assocType, proto); + + for (auto *inheritedProto : Context.getInheritedProtocols(proto)) { + for (auto *assocType : inheritedProto->getAssociatedTypeMembers()) + addAssociatedType(assocType, proto); + } +} + +/// For an associated type T in a protocol P, we add a rewrite rule: +/// +/// [P].T => [P:T] +/// +/// Intuitively, this means "if a type conforms to P, it has a nested type +/// named T". +void RuleBuilder::addAssociatedType(const AssociatedTypeDecl *type, + const ProtocolDecl *proto) { + MutableTerm lhs; + lhs.add(Symbol::forProtocol(proto, Context)); + lhs.add(Symbol::forName(type->getName(), Context)); + + MutableTerm rhs; + rhs.add(Symbol::forAssociatedType(proto, type->getName(), Context)); + + PermanentRules.emplace_back(lhs, rhs); +} + +/// Lowers a desugared generic requirement to a rewrite rule. +/// +/// Convert a requirement to a rule and add it to the builder. +/// +/// The types in the requirement must be canonical. +/// +/// If \p proto is null and \p substitutions is None, this is a generic +/// requirement from the top-level generic signature. The added rewrite +/// rule will be rooted in a generic parameter symbol. +/// +/// If \p proto is non-null, this is a generic requirement in the protocol's +/// requirement signature. The added rewrite rule will be rooted in a +/// protocol symbol. +/// +/// If \p substitutions is not None, this is a conditional requirement +/// added by conditional requirement inference. The added rewrite rule +/// will be added in the corresponding term from the substitution array. +void RuleBuilder::addRequirement(const Requirement &req, + const ProtocolDecl *proto, + Optional> substitutions, + Optional requirementID) { + if (Dump) { + llvm::dbgs() << "+ "; + req.dump(llvm::dbgs()); + llvm::dbgs() << "\n"; + } + + assert(!substitutions.hasValue() || proto == nullptr && "Can't have both"); + + // Compute the left hand side. + auto subjectType = CanType(req.getFirstType()); + auto subjectTerm = (substitutions + ? Context.getRelativeTermForType( + subjectType, *substitutions) + : Context.getMutableTermForType( + subjectType, proto)); + + // Compute the right hand side. + MutableTerm constraintTerm; + + switch (req.getKind()) { + case RequirementKind::Conformance: { + // A conformance requirement T : P becomes a rewrite rule + // + // T.[P] == T + // + // Intuitively, this means "any type ending with T conforms to P". + auto *proto = req.getProtocolDecl(); + + constraintTerm = subjectTerm; + constraintTerm.add(Symbol::forProtocol(proto, Context)); + break; + } + + case RequirementKind::Superclass: { + // A superclass requirement T : C becomes a rewrite rule + // + // T.[superclass: C] => T + auto otherType = CanType(req.getSecondType()); + + // Build the symbol [superclass: C]. + SmallVector result; + otherType = (substitutions + ? Context.getRelativeSubstitutionSchemaFromType( + otherType, *substitutions, result) + : Context.getSubstitutionSchemaFromType( + otherType, proto, result)); + auto superclassSymbol = Symbol::forSuperclass(otherType, result, Context); + + // Build the term T.[superclass: C]. + constraintTerm = subjectTerm; + constraintTerm.add(superclassSymbol); + break; + } + + case RequirementKind::Layout: { + // A layout requirement T : L becomes a rewrite rule + // + // T.[layout: L] == T + constraintTerm = subjectTerm; + constraintTerm.add(Symbol::forLayout(req.getLayoutConstraint(), Context)); + break; + } + + case RequirementKind::SameType: { + auto otherType = CanType(req.getSecondType()); + + if (!otherType->isTypeParameter()) { + // A concrete same-type requirement T == C becomes a + // rewrite rule + // + // T.[concrete: C] => T + SmallVector result; + otherType = (substitutions + ? Context.getRelativeSubstitutionSchemaFromType( + otherType, *substitutions, result) + : Context.getSubstitutionSchemaFromType( + otherType, proto, result)); + + constraintTerm = subjectTerm; + constraintTerm.add(Symbol::forConcreteType(otherType, result, Context)); + break; + } + + constraintTerm = (substitutions + ? Context.getRelativeTermForType( + otherType, *substitutions) + : Context.getMutableTermForType( + otherType, proto)); + break; + } + } + + RequirementRules.emplace_back( + std::move(subjectTerm), std::move(constraintTerm), + requirementID); +} + +void RuleBuilder::addRequirement(const StructuralRequirement &req, + const ProtocolDecl *proto) { + WrittenRequirements.push_back(req); + unsigned requirementID = WrittenRequirements.size() - 1; + addRequirement(req.req.getCanonical(), proto, /*substitutions=*/None, + requirementID); +} + +/// Lowers a protocol typealias to a rewrite rule. +void RuleBuilder::addTypeAlias(const ProtocolTypeAlias &alias, + const ProtocolDecl *proto) { + // Build the term [P].T, where P is the protocol and T is a name symbol. + MutableTerm subjectTerm; + subjectTerm.add(Symbol::forProtocol(proto, Context)); + subjectTerm.add(Symbol::forName(alias.getName(), Context)); + + auto constraintType = alias.getUnderlyingType()->getCanonicalType(); + MutableTerm constraintTerm; + + if (constraintType->isTypeParameter()) { + // If the underlying type of the typealias is a type parameter X, build + // a rule [P].T => X, where X, + constraintTerm = Context.getMutableTermForType( + constraintType, proto); + } else { + // If the underlying type of the typealias is a concrete type C, build + // a rule [P].T.[concrete: C] => [P].T. + constraintTerm = subjectTerm; + + SmallVector result; + auto concreteType = + Context.getSubstitutionSchemaFromType( + constraintType, proto, result); + + constraintTerm.add(Symbol::forConcreteType(concreteType, result, Context)); + } + + RequirementRules.emplace_back(subjectTerm, constraintTerm, + /*requirementID=*/None); +} + +/// If we haven't seen this protocol yet, save it for later so that we can +/// import the rewrite rules from its connected component. +void RuleBuilder::addReferencedProtocol(const ProtocolDecl *proto) { + if (ReferencedProtocols.insert(proto).second) + ProtocolsToImport.push_back(proto); +} + +/// Compute the transitive closure of the set of all protocols referenced from +/// the right hand sides of conformance requirements, and convert their +/// requirements to rewrite rules. +void RuleBuilder::collectRulesFromReferencedProtocols() { + // Compute the transitive closure. + unsigned i = 0; + while (i < ProtocolsToImport.size()) { + auto *proto = ProtocolsToImport[i++]; + for (auto *depProto : proto->getProtocolDependencies()) { + addReferencedProtocol(depProto); + } + } + + // If this is a rewrite system for a generic signature, add rewrite rules for + // each referenced protocol. + // + // if this is a rewrite system for a connected component of the protocol + // dependency graph, add rewrite rules for each referenced protocol not part + // of this connected component. + + // First, collect all unique requirement machines, one for each connected + // component of each referenced protocol. + llvm::DenseSet machines; + + // Now visit each subordinate requirement machine pull in its rules. + for (auto *proto : ProtocolsToImport) { + // This will trigger requirement signature computation for this protocol, + // if neccessary, which will cause us to re-enter into a new RuleBuilder + // instace under RuleBuilder::initWithProtocolWrittenRequirements(). + if (Dump) { + llvm::dbgs() << "importing protocol " << proto->getName() << "\n"; + } + + auto *machine = Context.getRequirementMachine(proto); + if (!machines.insert(machine).second) { + // We've already seen this connected component. + continue; + } + + // We grab the machine's local rules, not *all* of its rules, to avoid + // duplicates in case multiple machines share a dependency on a downstream + // protocol connected component. + auto localRules = machine->getLocalRules(); + ImportedRules.insert(ImportedRules.end(), + localRules.begin(), + localRules.end()); + } +} diff --git a/lib/AST/RequirementMachine/RuleBuilder.h b/lib/AST/RequirementMachine/RuleBuilder.h new file mode 100644 index 0000000000000..97ac2bf672b07 --- /dev/null +++ b/lib/AST/RequirementMachine/RuleBuilder.h @@ -0,0 +1,125 @@ +//===--- RuleBuilder.h - Lowering desugared requirements to rules ---------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RULEBUILDER_H +#define SWIFT_RULEBUILDER_H + +#include "swift/AST/ASTContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallVector.h" +#include +#include "RewriteContext.h" +#include "Rule.h" +#include "Symbol.h" +#include "Term.h" + +namespace llvm { + class raw_ostream; +} + +namespace swift { + +class AssociatedTypeDecl; +class ProtocolDecl; +class ProtocolTypeAlias; +class Requirement; + +namespace rewriting { + +/// A utility class for bulding rewrite rules from the top-level requirements +/// of a generic signature. +/// +/// This also collects requirements from the transitive closure of all protocols +/// appearing on the right hand side of conformance requirements. +struct RuleBuilder { + RewriteContext &Context; + + /// The transitive closure of all protocols appearing on the right hand + /// side of conformance requirements. + llvm::DenseSet &ReferencedProtocols; + + /// A subset of the above in insertion order, consisting of the protocols + /// whose rules we are going to import. + /// + /// If this is a rewrite system built from a generic signature, this vector + /// contains all elements in the above set. + /// + /// If this is a rewrite system built from a strongly connected component + /// of the protocol, this vector contains all elements in the above set + /// except for the protocols belonging to the component representing the + /// rewrite system itself; those protocols are added directly instead of + /// being imported. + std::vector ProtocolsToImport; + + /// The rules representing a complete rewrite system for the above vector, + /// pulled in by collectRulesFromReferencedProtocols(). + std::vector ImportedRules; + + /// New rules to add which will be marked 'permanent'. These are rules for + /// introducing associated types, and relationships between layout, + /// superclass and concrete type symbols. They are not eliminated by + /// homotopy reduction, since they are always added when the rewrite system + /// is built. + std::vector> PermanentRules; + + /// New rules derived from requirements written by the user, which can be + /// eliminated by homotopy reduction. + std::vector>> + RequirementRules; + + /// Requirements written in source code. The requirement ID in the above + /// \c RequirementRules vector is an index into this array. + std::vector WrittenRequirements; + + /// Enables debugging output. Controlled by the -dump-requirement-machine + /// frontend flag. + unsigned Dump : 1; + + /// Used to ensure the initWith*() methods are only called once. + unsigned Initialized : 1; + + RuleBuilder(RewriteContext &ctx, + llvm::DenseSet &referencedProtocols) + : Context(ctx), ReferencedProtocols(referencedProtocols) { + Dump = ctx.getASTContext().LangOpts.DumpRequirementMachine; + Initialized = 0; + } + + void initWithGenericSignatureRequirements(ArrayRef requirements); + void initWithWrittenRequirements(ArrayRef requirements); + void initWithProtocolSignatureRequirements(ArrayRef proto); + void initWithProtocolWrittenRequirements(ArrayRef proto); + void initWithConditionalRequirements(ArrayRef requirements, + ArrayRef substitutions); + void addReferencedProtocol(const ProtocolDecl *proto); + void collectRulesFromReferencedProtocols(); + +private: + void addPermanentProtocolRules(const ProtocolDecl *proto); + void addAssociatedType(const AssociatedTypeDecl *type, + const ProtocolDecl *proto); + void addRequirement(const Requirement &req, + const ProtocolDecl *proto, + Optional> substitutions=None, + Optional requirementID=None); + void addRequirement(const StructuralRequirement &req, + const ProtocolDecl *proto); + void addTypeAlias(const ProtocolTypeAlias &alias, + const ProtocolDecl *proto); +}; + +} // end namespace rewriting + +} // end namespace swift + +#endif From d90e8122aa1b5feefdafc629441ca80f2ae75a27 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 11:15:00 -0400 Subject: [PATCH 132/242] RequirementMachine: Factor out duplicated lookupConcreteNestedType() utility --- lib/AST/CMakeLists.txt | 1 + .../ConcreteContraction.cpp | 35 +++----------- .../GenericSignatureQueries.cpp | 24 +--------- lib/AST/RequirementMachine/NameLookup.cpp | 46 +++++++++++++++++++ lib/AST/RequirementMachine/NameLookup.h | 38 +++++++++++++++ 5 files changed, 93 insertions(+), 51 deletions(-) create mode 100644 lib/AST/RequirementMachine/NameLookup.cpp create mode 100644 lib/AST/RequirementMachine/NameLookup.h diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index dc334dc2eb148..2ae50dc1e571d 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -83,6 +83,7 @@ add_swift_host_library(swiftAST STATIC RequirementMachine/InterfaceType.cpp RequirementMachine/KnuthBendix.cpp RequirementMachine/MinimalConformances.cpp + RequirementMachine/NameLookup.cpp RequirementMachine/NormalizeRewritePath.cpp RequirementMachine/PropertyMap.cpp RequirementMachine/PropertyRelations.cpp diff --git a/lib/AST/RequirementMachine/ConcreteContraction.cpp b/lib/AST/RequirementMachine/ConcreteContraction.cpp index c79f43b7de785..a90bb78cd48e0 100644 --- a/lib/AST/RequirementMachine/ConcreteContraction.cpp +++ b/lib/AST/RequirementMachine/ConcreteContraction.cpp @@ -146,6 +146,7 @@ #include "swift/AST/Types.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" +#include "NameLookup.h" #include "RequirementLowering.h" using namespace swift; @@ -182,30 +183,6 @@ class ConcreteContraction { } // end namespace -/// Find the most canonical member type of \p decl named \p name, using the -/// canonical type order. -static TypeDecl *lookupConcreteNestedType(ModuleDecl *module, - NominalTypeDecl *decl, - Identifier name) { - SmallVector foundMembers; - module->lookupQualified( - decl, DeclNameRef(name), - NL_QualifiedDefault | NL_OnlyTypes | NL_ProtocolMembers, - foundMembers); - - SmallVector concreteDecls; - for (auto member : foundMembers) - concreteDecls.push_back(cast(member)); - - if (concreteDecls.empty()) - return nullptr; - - return *std::min_element(concreteDecls.begin(), concreteDecls.end(), - [](TypeDecl *type1, TypeDecl *type2) { - return TypeDecl::compare(type1, type2) < 0; - }); -} - /// A re-implementation of Type::subst() that also handles unresolved /// DependentMemberTypes by performing name lookup into the base type. /// @@ -267,12 +244,12 @@ Optional ConcreteContraction::substTypeParameter( return None; } - auto *module = decl->getParentModule(); - // An unresolved DependentMemberType stores an identifier. Handle this // by performing a name lookup into the base type. - auto *typeDecl = lookupConcreteNestedType(module, decl, - memberType->getName()); + SmallVector concreteDecls; + lookupConcreteNestedType(decl, memberType->getName(), concreteDecls); + + auto *typeDecl = findBestConcreteNestedType(concreteDecls); if (typeDecl == nullptr) { // The base type doesn't contain a member type with this name, in which // case the requirement remains unsubstituted. @@ -285,7 +262,7 @@ Optional ConcreteContraction::substTypeParameter( // Substitute the base type into the member type. auto subMap = (*substBaseType)->getContextSubstitutionMap( - module, typeDecl->getDeclContext()); + decl->getParentModule(), typeDecl->getDeclContext()); return typeDecl->getDeclaredInterfaceType().subst(subMap); } diff --git a/lib/AST/RequirementMachine/GenericSignatureQueries.cpp b/lib/AST/RequirementMachine/GenericSignatureQueries.cpp index 7813c3fc53fbb..18a56f95f373d 100644 --- a/lib/AST/RequirementMachine/GenericSignatureQueries.cpp +++ b/lib/AST/RequirementMachine/GenericSignatureQueries.cpp @@ -14,13 +14,13 @@ // Use those methods instead of calling into the RequirementMachine directly. // //===----------------------------------------------------------------------===// + #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Module.h" -#include "llvm/ADT/TinyPtrVector.h" #include - +#include "NameLookup.h" #include "RequirementMachine.h" using namespace swift; @@ -609,26 +609,6 @@ RequirementMachine::getConformanceAccessPath(Type type, } } -static void lookupConcreteNestedType(NominalTypeDecl *decl, - Identifier name, - SmallVectorImpl &concreteDecls) { - SmallVector foundMembers; - decl->getParentModule()->lookupQualified( - decl, DeclNameRef(name), - NL_QualifiedDefault | NL_OnlyTypes | NL_ProtocolMembers, - foundMembers); - for (auto member : foundMembers) - concreteDecls.push_back(cast(member)); -} - -static TypeDecl * -findBestConcreteNestedType(SmallVectorImpl &concreteDecls) { - return *std::min_element(concreteDecls.begin(), concreteDecls.end(), - [](TypeDecl *type1, TypeDecl *type2) { - return TypeDecl::compare(type1, type2) < 0; - }); -} - TypeDecl * RequirementMachine::lookupNestedType(Type depType, Identifier name) const { auto term = Context.getMutableTermForType(depType->getCanonicalType(), diff --git a/lib/AST/RequirementMachine/NameLookup.cpp b/lib/AST/RequirementMachine/NameLookup.cpp new file mode 100644 index 0000000000000..d4f75c62a9289 --- /dev/null +++ b/lib/AST/RequirementMachine/NameLookup.cpp @@ -0,0 +1,46 @@ +//===--- NameLookup.cpp - Name lookup utilities ---------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "NameLookup.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Module.h" +#include "llvm/ADT/SmallVector.h" +#include + +using namespace swift; +using namespace rewriting; + +void +swift::rewriting::lookupConcreteNestedType( + NominalTypeDecl *decl, + Identifier name, + SmallVectorImpl &concreteDecls) { + SmallVector foundMembers; + decl->getParentModule()->lookupQualified( + decl, DeclNameRef(name), + NL_QualifiedDefault | NL_OnlyTypes | NL_ProtocolMembers, + foundMembers); + for (auto member : foundMembers) + concreteDecls.push_back(cast(member)); +} + +TypeDecl * +swift::rewriting::findBestConcreteNestedType( + SmallVectorImpl &concreteDecls) { + if (concreteDecls.empty()) + return nullptr; + + return *std::min_element(concreteDecls.begin(), concreteDecls.end(), + [](TypeDecl *type1, TypeDecl *type2) { + return TypeDecl::compare(type1, type2) < 0; + }); +} diff --git a/lib/AST/RequirementMachine/NameLookup.h b/lib/AST/RequirementMachine/NameLookup.h new file mode 100644 index 0000000000000..d44e501aba3a0 --- /dev/null +++ b/lib/AST/RequirementMachine/NameLookup.h @@ -0,0 +1,38 @@ +//===--- NameLookup.h - Name lookup utilities -------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RQM_NAMELOOKUP_H +#define SWIFT_RQM_NAMELOOKUP_H + +#include "llvm/ADT/SmallVector.h" + +namespace swift { + +class Identifier; +class NominalTypeDecl; +class TypeDecl; + +namespace rewriting { + +void lookupConcreteNestedType( + NominalTypeDecl *decl, + Identifier name, + llvm::SmallVectorImpl &concreteDecls); + +TypeDecl *findBestConcreteNestedType( + llvm::SmallVectorImpl &concreteDecls); + +} // end namespace rewriting + +} // end namespace swift + +#endif \ No newline at end of file From 60950655901c1399ed44cc32a9f21ea417e79939 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 11:32:33 -0400 Subject: [PATCH 133/242] RequirementMachine: Add a performance/diagnostics test case --- test/Generics/rdar51908331.swift | 109 +++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 test/Generics/rdar51908331.swift diff --git a/test/Generics/rdar51908331.swift b/test/Generics/rdar51908331.swift new file mode 100644 index 0000000000000..8f1400a5113ac --- /dev/null +++ b/test/Generics/rdar51908331.swift @@ -0,0 +1,109 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on -requirement-machine-abstract-signatures=on + +// Reduced from https://github.com/plx/HDXLSIMDSupport. + +protocol PA: PB, PC, PD {} + +protocol PB: PE where A2 == A3, A4 == A2, A5 == A6, A1 == Self {} +// expected-warning@-1 {{redundant same-type constraint 'Self.A2' == 'Self.A3'}} +// expected-warning@-2 {{redundant same-type constraint 'Self.A5' == 'Self.A6'}} + +protocol PC: PG where A5 == (A2, A2), A3 == A7.A8 {} + +protocol PD: PG where A6 == (A3, A3), A2 == A7.A8 {} + +protocol PE: PF where A1.A1 == Self {} + +protocol PF: PG { + associatedtype A1: PF where A1.A7 == A7, A1.A2 == A3, A1.A3 == A2, A1.A5 == A6, A1.A6 == A5 + // expected-warning@-1 {{redundant same-type constraint 'Self.A1.A7' == 'Self.A7'}} +} + +protocol PG: PH where A7: PI, A7: BinaryFloatingPoint { + associatedtype A2: PJ where A2.A7 == A7 + associatedtype A3: PJ where A3.A7 == A7 + associatedtype A4: PJ where A4.A7 == A7 + associatedtype A5 + associatedtype A6 +} + +protocol PH { + associatedtype A7: SIMDScalar +} + +protocol PI { + associatedtype A8 where A8.A7 == Self // expected-warning {{redundant same-type constraint 'Self.A8.A7' == 'Self'}} + associatedtype A9 where A9.A7 == Self, A9.A8 == A8 + associatedtype A10 where A10.A7 == Self, A10.A8 == A8, A10.A9 == A9 // expected-warning {{redundant same-type constraint 'Self.A10.A7' == 'Self'}} + associatedtype A11 where A11.A7 == Self, A11.A10 == A10 // expected-warning {{redundant same-type constraint 'Self.A11.A7' == 'Self'}} + associatedtype A12 where A12.A7 == Self, A12.A11 == A11 // expected-warning {{redundant same-type constraint 'Self.A12.A7' == 'Self'}} + associatedtype A13 where A13.A7 == Self, A13.A12 == A12 // expected-warning {{redundant same-type constraint 'Self.A13.A7' == 'Self'}} + associatedtype A14: PK where A14.A7 == Self, A14.A13 == A13 // expected-warning {{redundant same-type constraint 'Self.A14.A7' == 'Self'}} +} + +protocol PJ: SIMD, PH where Scalar == A7 {} + +protocol PK: SIMD, PH where Scalar == A7 { + associatedtype A13: PL where A13.Scalar == Scalar +} + +protocol PL: SIMD, PH where Scalar == A7 { + associatedtype A12: PM where A12.Scalar == Scalar +} + +protocol PM: SIMD, PH where Scalar == A7 { + associatedtype A11: PN where A11.Scalar == Scalar +} + +protocol PN: SIMD, PH where Scalar == A7 { + associatedtype A10: PO where A10.Scalar == Scalar +} + +protocol PO: SIMD, PH where Scalar == A7 { + associatedtype A8: PQ where A8.Scalar == Scalar + associatedtype A9: PP where A9.Scalar == Scalar +} + +protocol PP: SIMD, PH where Scalar == A7 { + associatedtype A8: PQ where A8.Scalar == Scalar +} + +protocol PQ: SIMD, PH where Scalar == A7 {} + +func sameType(_: T, _: T) {} + +func testPI1(_: T) { + sameType(T.A8.A7.self, T.self) +} + +func testPI2(_: T) { + sameType(T.A10.A7.self, T.self) +} + +func testPI3(_: T) { + sameType(T.A11.A7.self, T.self) +} + +func testPI4(_: T) { + sameType(T.A12.A7.self, T.self) +} + +func testPI5(_: T) { + sameType(T.A13.A7.self, T.self) +} + +func testPI6(_: T) { + sameType(T.A14.A7.self, T.self) +} + +func testPF1(_: T) { + sameType(T.A1.A7.self, T.A7.self) +} + +func testPB1(_: T) { + sameType(T.A2.self, T.A3.self) +} + +func testPB2(_: T) { + sameType(T.A5.self, T.A6.self) +} From 6e6c8c29a5db90dba150898b9c8297c582f8a7d7 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 12:03:19 -0400 Subject: [PATCH 134/242] RequirementMachine: Only check local rules against maximum rule limit Completion has a maximum rule limit since the Knuth-Bendix algorithm is a semi-decision procedure that does not terminate on all inputs. However, this means that generic signatures which import a large number of complex protocols can hit the limit even if they are convergent. Since imported rules are essentially free, ignore them here, to avoid having to increase the limit by hand. Now the default limit is 4000 local rules per requirement machine; it seems implausible that you would exceed this, but if anyone has an example we can bump the limit again. --- lib/AST/RequirementMachine/KnuthBendix.cpp | 2 +- lib/AST/RequirementMachine/RequirementMachine.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/AST/RequirementMachine/KnuthBendix.cpp b/lib/AST/RequirementMachine/KnuthBendix.cpp index 471af4576dba5..1081218e468bc 100644 --- a/lib/AST/RequirementMachine/KnuthBendix.cpp +++ b/lib/AST/RequirementMachine/KnuthBendix.cpp @@ -391,7 +391,7 @@ RewriteSystem::computeConfluentCompletion(unsigned maxRuleCount, for (const auto &pair : resolvedCriticalPairs) { // Check if we've already done too much work. - if (Rules.size() > maxRuleCount) + if (getLocalRules().size() > maxRuleCount) return std::make_pair(CompletionResult::MaxRuleCount, Rules.size() - 1); if (!addRule(pair.LHS, pair.RHS, &pair.Path)) diff --git a/lib/AST/RequirementMachine/RequirementMachine.cpp b/lib/AST/RequirementMachine/RequirementMachine.cpp index b8ad293e39f3d..672a90d453de2 100644 --- a/lib/AST/RequirementMachine/RequirementMachine.cpp +++ b/lib/AST/RequirementMachine/RequirementMachine.cpp @@ -305,7 +305,7 @@ RequirementMachine::computeCompletion(RewriteSystem::ValidityPolicy policy) { } } - if (System.getRules().size() > MaxRuleCount) { + if (System.getLocalRules().size() > MaxRuleCount) { return std::make_pair(CompletionResult::MaxRuleCount, System.getRules().size() - 1); } From 209c5c735ba8c4957e981a72836103bc21635c8f Mon Sep 17 00:00:00 2001 From: Vincent Lee Date: Wed, 16 Mar 2022 11:16:34 -0700 Subject: [PATCH 135/242] Prevent linker errors when building dispatch with instrumentation (#41819) When building Swift with IRPGO, libdispatch needs the CMAKE_*_LINKER_FLAGS in order to properly statically link to the clang runtimes library. --- cmake/modules/Libdispatch.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/modules/Libdispatch.cmake b/cmake/modules/Libdispatch.cmake index eba84956c84ef..39b3e4bc531fd 100644 --- a/cmake/modules/Libdispatch.cmake +++ b/cmake/modules/Libdispatch.cmake @@ -89,6 +89,8 @@ foreach(sdk ${DISPATCH_SDKS}) ${SWIFT_LIBDISPATCH_COMPILER_TRIPLE_CMAKE_ARGS} -DCMAKE_C_FLAGS=${CMAKE_C_FLAGS} -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} + -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} + -DCMAKE_SHARED_LINKER_FLAGS=${CMAKE_SHARED_LINKER_FLAGS} -DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM} -DCMAKE_INSTALL_LIBDIR=lib -DCMAKE_INSTALL_PREFIX= From acc9c4a4b8d29cc5d0cffa22a13e4a25f99d013e Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Wed, 16 Mar 2022 11:20:53 -0700 Subject: [PATCH 136/242] Tests: Disable distributed_actor_localSystem on watchOS until its failure on that platform can be investigated. --- test/Distributed/Runtime/distributed_actor_localSystem.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/Distributed/Runtime/distributed_actor_localSystem.swift b/test/Distributed/Runtime/distributed_actor_localSystem.swift index 2f0fa92786486..83ea6d8b2e3af 100644 --- a/test/Distributed/Runtime/distributed_actor_localSystem.swift +++ b/test/Distributed/Runtime/distributed_actor_localSystem.swift @@ -10,6 +10,9 @@ // UNSUPPORTED: use_os_stdlib // UNSUPPORTED: back_deployment_runtime +// rdar://90373022 +// UNSUPPORTED: OS=watchos + import Distributed distributed actor Worker { From febdf30e5b8273e0782f505b963c0c0a7ce70247 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 23 Feb 2022 17:08:59 -0800 Subject: [PATCH 137/242] [SSADestroyHoisting] Tie barriers to lifetimes. Only respect deinit barriers when lexical lifetimes are enabled. If they aren't, hoist destroy_addrs of all addresses aggressively regardless of whether doing so involves hoisting over deinit barriers. --- lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp index 471f58850cc96..3d52772961f04 100644 --- a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp @@ -729,6 +729,12 @@ bool hoistDestroys(SILValue root, bool ignoreDeinitBarriers, // The algorithm assumes no critical edges. assert(function->hasOwnership() && "requires OSSA"); + // If lexical lifetimes aren't enabled, then deinit barriers aren't respected. + auto &module = function->getModule(); + auto enableLexicalLifetimes = + module.getASTContext().SILOpts.supportsLexicalLifetimes(module); + ignoreDeinitBarriers = ignoreDeinitBarriers || !enableLexicalLifetimes; + return HoistDestroys(root, ignoreDeinitBarriers, remainingDestroyAddrs, deleter) .perform(); From 266668d01001e0fff0519bbd9dff7bee45d53bba Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 4 Mar 2022 18:40:05 -0800 Subject: [PATCH 138/242] [SSADestroyHoisting] Fold into sequences. Previously, destroy_addrs were folded into copy_addrs and load [copy]s to produce copy_addr [take]s and load [take]s respectively, but only if the source of the load/copy was exactly the address being destroyed. Generalize that to a single-block sequence of copy_addrs and load [copy]s of projections of the address being destroyed. --- .../Transforms/SSADestroyHoisting.cpp | 252 ++++++++++++++++-- test/SILOptimizer/hoist_destroy_addr.sil | 246 ++++++++++++++++- 2 files changed, 462 insertions(+), 36 deletions(-) diff --git a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp index 3d52772961f04..42e44192c63ea 100644 --- a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp @@ -89,6 +89,7 @@ #define DEBUG_TYPE "ssa-destroy-hoisting" +#include "swift/AST/Type.h" #include "swift/Basic/GraphNodeWorklist.h" #include "swift/Basic/SmallPtrSetVector.h" #include "swift/SIL/BasicBlockDatastructures.h" @@ -496,6 +497,9 @@ bool DeinitBarriers::DestroyReachability::checkReachablePhiBarrier( /// object. class HoistDestroys { SILValue storageRoot; + SILFunction *function; + SILModule &module; + TypeExpansionContext typeExpansionContext; bool ignoreDeinitBarriers; SmallPtrSetImpl &remainingDestroyAddrs; InstructionDeleter &deleter; @@ -509,7 +513,9 @@ class HoistDestroys { HoistDestroys(SILValue storageRoot, bool ignoreDeinitBarriers, SmallPtrSetImpl &remainingDestroyAddrs, InstructionDeleter &deleter) - : storageRoot(storageRoot), ignoreDeinitBarriers(ignoreDeinitBarriers), + : storageRoot(storageRoot), function(storageRoot->getFunction()), + module(function->getModule()), typeExpansionContext(*function), + ignoreDeinitBarriers(ignoreDeinitBarriers), remainingDestroyAddrs(remainingDestroyAddrs), deleter(deleter), destroyMergeBlocks(getFunction()) {} @@ -518,11 +524,20 @@ class HoistDestroys { protected: SILFunction *getFunction() const { return storageRoot->getFunction(); } - bool foldBarrier(SILInstruction *barrier, SILValue accessScope); + bool foldBarrier(SILInstruction *barrier, const AccessStorage &storage, + const DeinitBarriers &deinitBarriers); - bool foldBarrier(SILInstruction *barrier, const KnownStorageUses &knownUses, + bool foldBarrier(SILInstruction *barrier, const AccessStorage &storage, + const KnownStorageUses &knownUses, const DeinitBarriers &deinitBarriers); + bool checkFoldingBarrier(SILInstruction *instruction, + SmallVectorImpl &loads, + SmallVectorImpl &copies, + SmallPtrSetImpl &leaves, + const AccessStorage &storage, + const DeinitBarriers &deinitBarriers); + void insertDestroy(SILInstruction *barrier, SILInstruction *insertBefore, const KnownStorageUses &knownUses); @@ -531,7 +546,8 @@ class HoistDestroys { void createSuccessorDestroys(SILBasicBlock *barrierBlock); - bool rewriteDestroys(const KnownStorageUses &knownUses, + bool rewriteDestroys(const AccessStorage &storage, + const KnownStorageUses &knownUses, const DeinitBarriers &deinitBarriers); void mergeDestroys(SILBasicBlock *mergeBlock); @@ -553,16 +569,17 @@ bool HoistDestroys::perform() { deinitBarriers.compute(); // No SIL changes happen before rewriting. - return rewriteDestroys(knownUses, deinitBarriers); + return rewriteDestroys(storage, knownUses, deinitBarriers); } -bool HoistDestroys::rewriteDestroys(const KnownStorageUses &knownUses, +bool HoistDestroys::rewriteDestroys(const AccessStorage &storage, + const KnownStorageUses &knownUses, const DeinitBarriers &deinitBarriers) { // Place a new destroy after each barrier instruction. for (SILInstruction *barrier : deinitBarriers.barriers) { auto *barrierBlock = barrier->getParent(); if (barrier != barrierBlock->getTerminator()) { - if (!foldBarrier(barrier, knownUses, deinitBarriers)) + if (!foldBarrier(barrier, storage, knownUses, deinitBarriers)) insertDestroy(barrier, barrier->getNextInstruction(), knownUses); continue; } @@ -610,30 +627,220 @@ bool HoistDestroys::rewriteDestroys(const KnownStorageUses &knownUses, return deleter.hadCallbackInvocation(); } -bool HoistDestroys::foldBarrier(SILInstruction *barrier, SILValue storageRoot) { - if (auto *load = dyn_cast(barrier)) { - if (stripAccessMarkers(load->getOperand()) == - stripAccessMarkers(storageRoot)) { +/// Try to fold the destroy_addr with the specified barrier, or a backwards +/// sequence of instructions that it begins. +/// +/// Do the following kinds of folds: +/// +/// - loads: +/// given: load [copy] %addr +/// destroy_addr %addr +/// yield: load [take] +/// - copy_addrs: +/// given: copy_addr %addr to ... +/// destroy_addr %addr +/// yield: copy_addr [take] %addr +/// +/// Additionally, generalize this to subobjects. If there is a sequence of +/// copy_addrs and loads that covers all the subobjects of %addr. Given +/// projections %subobject_1 and %subobject_2 out of %addr which fully cover all +/// the non-trivial fields of the recursive type-tree of %addr, fold +/// +/// load [copy] %subobject_1 +/// copy_addr %subobject_2 to ... +/// destroy_addr %addr +/// +/// into +/// +/// load [take] %subobject_1 +/// copy_addr [take] %subobject_2 to ... +/// +/// so long as all the loads and copy_addrs occur within the same block. +bool HoistDestroys::foldBarrier(SILInstruction *barrier, + const AccessStorage &storage, + const DeinitBarriers &deinitBarriers) { + + // The load [copy]s which will be folded into load [take]s if folding is + // possible. + llvm::SmallVector loads; + // The copy_addrs which will be folded into copy_addr [take]s if folding is + // possible. + llvm::SmallVector copies; + + // The non-trivial storage leaves of the root storage all of which must be + // destroyed exactly once in the sequence of instructions prior to the + // destroy_addr in order for folding to occur. + llvm::SmallPtrSet leaves; + + visitProductLeafAccessPathNodes(storageRoot, typeExpansionContext, module, + [&](AccessPath::PathNode node, SILType ty) { + if (ty.isTrivial(*function)) + return; + leaves.insert(node); + }); + + for (auto *instruction = barrier; instruction != nullptr; + instruction = instruction->getPreviousInstruction()) { + if (checkFoldingBarrier(instruction, loads, copies, leaves, storage, + deinitBarriers)) + return false; + + // If we have load [copy]s or copy_addrs of projections out of the root + // storage that cover all non-trivial product leaves, then we can fold! + // + // Stop looking for instructions to fold. + if (leaves.empty()) + break; + } + + if (!leaves.empty()) + return false; + + for (auto *load : loads) { + assert(load->getOwnershipQualifier() == LoadOwnershipQualifier::Copy); + load->setOwnershipQualifier(LoadOwnershipQualifier::Take); + } + for (auto *copy : copies) { + assert(!copy->isTakeOfSrc()); + copy->setIsTakeOfSrc(IsTake); + } + + return true; +} + +/// Whether the specified instruction is a barrier to folding. +/// +/// TODO: This is a bit more conservative that it needs to be in a couple of +/// ways: +/// +/// (1) even if we've already seen a leaf, we could still fold, in certain +/// cases, we should be able to fold anyway. For example, given projections +/// %p1 and %p2 of some root storage %a, in the following scenario: +/// +/// %p1 = %a +/// %p2 = %a +/// %v1 = load [copy] %p1 +/// %v2_1 = load [copy] %p2 +/// %v2_1 = load [copy] %p2 +/// destroy_addr %a +/// +/// we could fold destroy_addr %a into the first load [copy] %p2 and the +/// load [copy] %p1: +/// +/// %v1 = load [take] %p1 +/// %v2_1 = load [copy] %p2 +/// %v2_2 = load [take] %p1 +/// +/// And indeed we can do that for loads from a subprojection %p2_sub of +/// %p2; the following +/// +/// %v1 = load [copy] %p1 +/// %v2_sub = load [copy] %p2_sub +/// %v2 = load [copy] %p2 +/// +/// could be folded to +/// +/// %v1 = load [take] %p1 +/// %v2_sub = load [copy] %p2_sub +/// %v2 = load [take] %p2 +/// +/// (2) We should be able to continue folding over a load [trivial] so long as +/// the instructions that we're folding with don't destroy an aggregate that +/// contains the projection which is the target of the load [trivial]. For +/// example, given +/// +/// %addr = alloc_stack %(X, I) +/// %x_addr = tuple_element_addr %addr : $*(X, I), 0 +/// %i_addr = tuple_element_addr %addr : $*(X, I), 1 +/// %x = load [copy] %x_addr : $*X +/// %i = load [trivial] %i_addr : $*I +/// destroy_addr %addr +/// +/// we should be able to fold the destroy_addr of the tuple with the load [copy] +/// and ignore the load [trivial]. +/// +/// Doing this is complicated by the fact that we can't ignore the load +/// [trivial] if the load [copy] is of the whole tuple. If we have instead +/// +/// %addr = alloc_stack %(X, I) +/// %x_addr = tuple_element_addr %addr : $*(X, I), 0 +/// %i_addr = tuple_element_addr %addr : $*(X, I), 1 +/// %x = load [copy] %addr : $*(X, I) +/// %i = load [trivial] %i_addr : $*I +/// destroy_addr %addr +/// +/// then we cannot fold. If we did, we would end up with invalid SIL: +/// +/// %x = load [take] %addr +/// %i = load [trivial] %i_addr +bool HoistDestroys::checkFoldingBarrier( + SILInstruction *instruction, SmallVectorImpl &loads, + SmallVectorImpl &copies, + SmallPtrSetImpl &leaves, const AccessStorage &storage, + const DeinitBarriers &deinitBarriers) { + // The address of a projection out of the root storage which would be + // folded if folding is possible. + // + // If no such address is found, we need to check whether the instruction + // is a barrier. + SILValue address; + if (auto *load = dyn_cast(instruction)) { + auto loadee = load->getOperand(); + auto accessPath = AccessPath::compute(loadee); + if (accessPath.getStorage().hasIdenticalStorage(storage)) { if (load->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { - load->setOwnershipQualifier(LoadOwnershipQualifier::Take); - return true; + address = loadee; + loads.push_back(load); } else { - assert(load->getOperand()->getType().isTrivial(*load->getFunction())); - return false; + assert(loadee->getType().isTrivial(*load->getFunction())); + return true; } } + } else if (auto *copy = dyn_cast(instruction)) { + auto source = copy->getSrc(); + auto accessPath = AccessPath::compute(source); + if (accessPath.getStorage().hasIdenticalStorage(storage)) { + address = source; + copies.push_back(copy); + } } - if (auto *copy = dyn_cast(barrier)) { - if (stripAccessMarkers(copy->getSrc()) == stripAccessMarkers(storageRoot)) { - assert(!copy->isTakeOfSrc()); - copy->setIsTakeOfSrc(IsTake); + if (address) { + // We found a relevant instruction that is operating on a projection out + // of the root storage which would be folded if folding were possible. + // Find its nontrivial product leaves and remove them from the set of + // leaves of the root storage which we're wating to see. + bool alreadySawLeaf = false; + visitProductLeafAccessPathNodes(address, typeExpansionContext, module, + [&](AccessPath::PathNode node, SILType ty) { + if (ty.isTrivial(*function)) + return; + bool erased = leaves.erase(node); + alreadySawLeaf = + alreadySawLeaf || !erased; + }); + if (alreadySawLeaf) { + // We saw this non-trivial product leaf already. That means there are + // multiple load [copy]s or copy_addrs of at least one product leaf + // before (walking backwards from the hoisting point) there are + // instructions that load or copy from all the non-trivial leaves. + // Give up on folding. return true; } + } else if (deinitBarriers.isBarrier(instruction)) { + // We didn't find an instruction that was both + // - relevant (i.e. a copy_addr or a load [take]) + // - operating on a projection of the root storage + // Additionally: + // - we can't ignore whether it's a barrier + // - and it IS a barrier. + // We can't fold. + return true; } return false; } bool HoistDestroys::foldBarrier(SILInstruction *barrier, + const AccessStorage &storage, const KnownStorageUses &knownUses, const DeinitBarriers &deinitBarriers) { if (auto *eai = dyn_cast(barrier)) { @@ -645,13 +852,13 @@ bool HoistDestroys::foldBarrier(SILInstruction *barrier, while ((instruction = instruction->getPreviousInstruction())) { if (instruction == bai) return false; - if (foldBarrier(instruction, storageRoot)) + if (foldBarrier(instruction, storage, deinitBarriers)) return true; if (deinitBarriers.isBarrier(instruction)) return false; } } - return foldBarrier(barrier, storageRoot); + return foldBarrier(barrier, storage, deinitBarriers); } // \p barrier may be null if the destroy is at function entry. @@ -848,8 +1055,7 @@ void SSADestroyHoisting::run() { // We assume that the function is in reverse post order so visiting the // blocks and pushing begin_access as we see them and then popping them off // the end will result in hoisting inner begin_access' destroy_addrs first. - while (!bais.empty()) { - auto *bai = bais.pop_back_val(); + for (auto *bai : llvm::reverse(bais)) { changed |= hoistDestroys(bai, /*ignoreDeinitBarriers=*/true, remainingDestroyAddrs, deleter); } diff --git a/test/SILOptimizer/hoist_destroy_addr.sil b/test/SILOptimizer/hoist_destroy_addr.sil index f1d5b4589920e..4c946e47c3639 100644 --- a/test/SILOptimizer/hoist_destroy_addr.sil +++ b/test/SILOptimizer/hoist_destroy_addr.sil @@ -52,6 +52,24 @@ struct Int { @_hasStorage var _value : Builtin.Int64 } +struct I { + var value: Builtin.Int64 +} + +typealias TXXI = (X, X, I) + +struct SXXI { + var x_1: X + var x_2: X + var i: I +} + +struct STXXITXXII { + var txxi_1: TXXI + var txxi_2: TXXI + var i: I +} + sil @unknown : $@convention(thin) () -> () sil @use_S : $@convention(thin) (@in_guaranteed S) -> () @@ -372,8 +390,7 @@ entry(%addr : $*X, %instance : @owned $X): // CHECK-LABEL: sil [ossa] @hoist_upto_address_to_pointer_use : {{.*}} { // CHECK: address_to_pointer // CHECK: pointer_to_address -// CHECK: load [copy] -// CHECK: destroy_addr +// CHECK: load [take] // CHECK: tuple // CHECK-LABEL: } // end sil function 'hoist_upto_address_to_pointer_use' sil [ossa] @hoist_upto_address_to_pointer_use : $@convention(thin) (@in X) -> (@owned X) { @@ -444,12 +461,12 @@ entry(%instance : @owned $S): return %value : $S } -// Don't fold with a copy_addr of a struct_element_addr. +// Fold with a copy_addr of a struct_element_addr. // -// CHECK-LABEL: sil [ossa] @nofold_scoped_copy_addr_projection : {{.*}} { -// CHECK: load [copy] -// CHECK-LABEL: // end sil function 'nofold_scoped_copy_addr_projection' -sil [ossa] @nofold_scoped_copy_addr_projection : $@convention(thin) (@owned S) -> (@owned S) { +// CHECK-LABEL: sil [ossa] @fold_scoped_copy_addr_projection : {{.*}} { +// CHECK: load [take] +// CHECK-LABEL: // end sil function 'fold_scoped_copy_addr_projection' +sil [ossa] @fold_scoped_copy_addr_projection : $@convention(thin) (@owned S) -> (@owned S) { entry(%instance : @owned $S): %addr = alloc_stack $S %store_scope = begin_access [modify] [static] %addr : $*S @@ -465,12 +482,12 @@ entry(%instance : @owned $S): return %value : $S } -// Don't fold destroy of outer scope with struct_element_addr of inner scope. +// Fold destroy of outer scope with struct_element_addr of inner scope. // -// CHECK-LABEL: sil [ossa] @nofold_with_copy_addr_projection : {{.*}} { -// CHECK: copy_addr {{%[^,]+}} -// CHECK-LABEL: // end sil function 'nofold_with_copy_addr_projection' -sil [ossa] @nofold_with_copy_addr_projection : $@convention(thin) (@owned S) -> (@owned S) { +// CHECK-LABEL: sil [ossa] @fold_with_copy_addr_projection : {{.*}} { +// CHECK: copy_addr [take] {{%[^,]+}} +// CHECK-LABEL: // end sil function 'fold_with_copy_addr_projection' +sil [ossa] @fold_with_copy_addr_projection : $@convention(thin) (@owned S) -> (@owned S) { entry(%instance : @owned $S): %addr = alloc_stack $S %addr_2 = alloc_stack $S @@ -492,7 +509,7 @@ entry(%instance : @owned $S): return %value : $S } -// Don't fold destroy of outer scope with struct_element_addr of inner scope. +// Fold destroy of outer scope with struct_element_addr of inner scope. // // CHECK-LABEL: sil [ossa] @fold_scoped_destroy_with_scoped_copy_addr : {{.*}} { // CHECK: copy_addr [take] {{%[^,]+}} @@ -575,3 +592,206 @@ entry(%instance : @owned $X): dealloc_stack %addr_outer : $*X return %value : $X } + +// Fold with loads of immediate leaves of a tuple. +// +// CHECK-LABEL: sil [ossa] @fold_tuple_field_loads : {{.*}} { +// CHECK: load [take] +// CHECK: load [take] +// CHECK-LABEL: } // end sil function 'fold_tuple_field_loads' +sil [ossa] @fold_tuple_field_loads : $@convention(thin) (@owned (X, X, I)) -> @owned (X, X) { +entry(%instance : @owned $(X, X, I)): + %addr = alloc_stack $(X, X, I) + store %instance to [init] %addr : $*(X, X, I) + %first_addr = tuple_element_addr %addr : $*(X, X, I), 0 + %second_addr = tuple_element_addr %addr : $*(X, X, I), 1 + %first = load [copy] %first_addr : $*X + %second = load [copy] %second_addr : $*X + destroy_addr %addr : $*(X, X, I) + dealloc_stack %addr : $*(X, X, I) + %retval = tuple (%first : $X, %second : $X) + return %retval : $(X, X) +} + +// Fold with copy_addrs of immediate leaves of a struct. +// +// CHECK-LABEL: sil [ossa] @fold_struct_field_copy_addrs : $@convention(thin) (@owned SXXI) -> () { +// CHECK: copy_addr [take] +// CHECK: copy_addr [take] +// CHECK-LABEL: } // end sil function 'fold_struct_field_copy_addrs' +sil [ossa] @fold_struct_field_copy_addrs : $@convention(thin) (@owned SXXI) -> () { +entry(%instance : @owned $SXXI): + %addr = alloc_stack $SXXI + %x_1_alone = alloc_stack $X + %x_2_alone = alloc_stack $X + store %instance to [init] %addr : $*SXXI + %x_1_addr = struct_element_addr %addr : $*SXXI, #SXXI.x_1 + %x_2_addr = struct_element_addr %addr : $*SXXI, #SXXI.x_2 + copy_addr %x_1_addr to [initialization] %x_1_alone : $*X + copy_addr %x_2_addr to [initialization] %x_2_alone : $*X + destroy_addr %addr : $*SXXI + %barrier = function_ref @unknown : $@convention(thin) () -> () + apply %barrier() : $@convention(thin) () -> () + + destroy_addr %x_1_alone : $*X + destroy_addr %x_2_alone : $*X + dealloc_stack %x_2_alone : $*X + dealloc_stack %x_1_alone : $*X + dealloc_stack %addr : $*SXXI + %retval = tuple () + return %retval : $() +} + +// Fold with a mix of both copy_addrs and load [copy]s which are dealing +// exclusively non-trivial leaves. +// +// CHECK-LABEL: sil [ossa] @fold_struct_enclosing_tuples_leaf_fields_mixed_insts : {{.*}} { +// CHECK: copy_addr [take] +// CHECK: load [take] +// CHECK: copy_addr [take] +// CHECK: load [take] +// CHECK-LABEL: } // end sil function 'fold_struct_enclosing_tuples_leaf_fields_mixed_insts' +sil [ossa] @fold_struct_enclosing_tuples_leaf_fields_mixed_insts : $@convention(thin) (@owned STXXITXXII) -> @owned (X, X) { +entry(%instance : @owned $STXXITXXII): + %addr = alloc_stack $STXXITXXII + %txxi_1_alone = alloc_stack $X + %txxi_2_alone = alloc_stack $X + store %instance to [init] %addr : $*STXXITXXII + %txxi_1_addr = struct_element_addr %addr : $*STXXITXXII, #STXXITXXII.txxi_1 + %txxi_2_addr = struct_element_addr %addr : $*STXXITXXII, #STXXITXXII.txxi_2 + %txxi_1_x_0_addr = tuple_element_addr %txxi_1_addr : $*(X, X, I), 0 + %txxi_1_x_1_addr = tuple_element_addr %txxi_1_addr : $*(X, X, I), 1 + %txxi_2_x_0_addr = tuple_element_addr %txxi_2_addr : $*(X, X, I), 0 + %txxi_2_x_1_addr = tuple_element_addr %txxi_2_addr : $*(X, X, I), 1 + + copy_addr %txxi_1_x_0_addr to [initialization] %txxi_1_alone : $*X + %x1x1 = load [copy] %txxi_1_x_1_addr : $*X + copy_addr %txxi_2_x_0_addr to [initialization] %txxi_2_alone : $*X + %x2x1 = load [copy] %txxi_2_x_1_addr : $*X + destroy_addr %addr : $*STXXITXXII + %barrier = function_ref @unknown : $@convention(thin) () -> () + apply %barrier() : $@convention(thin) () -> () + + destroy_addr %txxi_1_alone : $*X + destroy_addr %txxi_2_alone : $*X + dealloc_stack %txxi_2_alone : $*X + dealloc_stack %txxi_1_alone : $*X + dealloc_stack %addr : $*STXXITXXII + %retval = tuple (%x1x1 : $X, %x2x1 : $X) + return %retval : $(X, X) +} + +// Fold with a mix of both copy_addrs and load [copy]s which are dealing with +// a mix of both leaves and intermediate nodes. +// +// CHECK-LABEL: sil [ossa] @fold_struct_enclosing_tuples_mixed_fields_mixed_insts : {{.*}} { +// CHECK: copy_addr [take] +// CHECK: load [take] +// CHECK: load [take] +// CHECK-LABEL: } // end sil function 'fold_struct_enclosing_tuples_mixed_fields_mixed_insts' +sil [ossa] @fold_struct_enclosing_tuples_mixed_fields_mixed_insts : $@convention(thin) (@owned STXXITXXII) -> @owned (X, (X, X, I)) { +entry(%instance : @owned $STXXITXXII): + %addr = alloc_stack $STXXITXXII + %txxi_1_alone = alloc_stack $X + store %instance to [init] %addr : $*STXXITXXII + %txxi_1_addr = struct_element_addr %addr : $*STXXITXXII, #STXXITXXII.txxi_1 + %txxi_2_addr = struct_element_addr %addr : $*STXXITXXII, #STXXITXXII.txxi_2 + %txxi_1_x_0_addr = tuple_element_addr %txxi_1_addr : $*(X, X, I), 0 + %txxi_1_x_1_addr = tuple_element_addr %txxi_1_addr : $*(X, X, I), 1 + + copy_addr %txxi_1_x_0_addr to [initialization] %txxi_1_alone : $*X + %x1x1 = load [copy] %txxi_1_x_1_addr : $*X + %txxi2 = load [copy] %txxi_2_addr : $*(X, X, I) + destroy_addr %addr : $*STXXITXXII + + %barrier = function_ref @unknown : $@convention(thin) () -> () + apply %barrier() : $@convention(thin) () -> () + destroy_addr %txxi_1_alone : $*X + dealloc_stack %txxi_1_alone : $*X + dealloc_stack %addr : $*STXXITXXII + %retval = tuple (%x1x1 : $X, %txxi2 : $(X, X, I)) + return %retval : $(X, (X, X, I)) +} + +// When walking backwards from the hoisting point, if we encounter multiple +// loads of the same nontrivial leaf/leaves BEFORE encountering all nontrivial +// leaves, we can't fold. +// +// TODO: Allow folding with the last of them if possible. +// CHECK-LABEL: sil [ossa] @nofold_multiple_nontrivial_leaf_loads : {{.*}} { +// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @owned $SXXI): +// CHECK: [[ADDR:%[^,]+]] = alloc_stack +// CHECK: load [copy] +// CHECK: load [copy] +// CHECK: load [copy] +// CHECK: destroy_addr [[ADDR]] +// CHECK-LABEL: } // end sil function 'nofold_multiple_nontrivial_leaf_loads' +sil [ossa] @nofold_multiple_nontrivial_leaf_loads : $@convention(thin) (@owned SXXI) -> @owned (X, X, X) { +entry(%instance : @owned $SXXI): + %addr = alloc_stack $SXXI + store %instance to [init] %addr : $*SXXI + + %x_1_addr = struct_element_addr %addr : $*SXXI, #SXXI.x_1 + %x_2_addr = struct_element_addr %addr : $*SXXI, #SXXI.x_2 + + %x_1 = load [copy] %x_1_addr : $*X + %x_2_1 = load [copy] %x_2_addr : $*X + %x_2_2 = load [copy] %x_2_addr : $*X + destroy_addr %addr : $*SXXI + + dealloc_stack %addr : $*SXXI + %retval = tuple (%x_1 : $X, %x_2_1 : $X, %x_2_2 : $X) + return %retval : $(X, X, X) +} + +// When walking backwards from the hoisting point, fold even if we encounter +// multiple loads of the same TRIVIAL leaf/leaves BEFORE encountering all +// nontrivial leaves. Contrast to nofold_multiple_nontrivial_leaf_loads. +// +// CHECK-LABEL: sil [ossa] @nofold_multiple_trivial_leaf_loads : {{.*}} { +// CHECK: load [copy] +// CHECK: load [copy] +// CHECK: load [trivial] +// CHECK: load [trivial] +// CHECK: destroy_addr +// CHECK-LABEL: } // end sil function 'nofold_multiple_trivial_leaf_loads' +sil [ossa] @nofold_multiple_trivial_leaf_loads : $@convention(thin) (@owned SXXI) -> @owned (I, I, X, X) { +entry(%instance : @owned $SXXI): + %addr = alloc_stack $SXXI + store %instance to [init] %addr : $*SXXI + + %x_1_addr = struct_element_addr %addr : $*SXXI, #SXXI.x_1 + %x_2_addr = struct_element_addr %addr : $*SXXI, #SXXI.x_2 + %i_addr = struct_element_addr %addr : $*SXXI, #SXXI.i + + %x_1 = load [copy] %x_1_addr : $*X + %x_2 = load [copy] %x_2_addr : $*X + %i_1 = load [trivial] %i_addr : $*I + %i_2 = load [trivial] %i_addr : $*I + destroy_addr %addr : $*SXXI + + dealloc_stack %addr : $*SXXI + %retval = tuple (%i_1 : $I, %i_2 : $I, %x_1 : $X, %x_2 : $X) + return %retval : $(I, I, X, X) +} + +// CHECK-LABEL: sil [ossa] @nofold_over_trivial_load_into_tuple_load_copy : {{.*}} { +// CHECK: load [copy] +// CHECK: load [trivial] +// CHECK: destroy_addr +// CHECK-LABEL: } // end sil function 'nofold_over_trivial_load_into_tuple_load_copy' +sil [ossa] @nofold_over_trivial_load_into_tuple_load_copy : $@convention(thin) (@owned (X, I)) -> @owned ((X, I), I) { +entry(%instance : @owned $(X, I)): + %addr = alloc_stack $(X, I) + store %instance to [init] %addr : $*(X, I) + + %i_addr = tuple_element_addr %addr : $*(X, I), 1 + + %copy = load [copy] %addr : $*(X, I) + %i_2 = load [trivial] %i_addr : $*I + destroy_addr %addr : $*(X, I) + + dealloc_stack %addr : $*(X, I) + %retval = tuple (%copy : $(X, I), %i_2 : $I) + return %retval : $((X, I), I) +} From 87d0c9fd6ee75081408e5ed55966e9ab39f5a8d5 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 9 Mar 2022 18:37:52 -0800 Subject: [PATCH 139/242] [SSADestroyHoisting] Bitcasts obstruct folding. Make loads and copy_addrs of casts of the underlying storage barriers to folding. Destroying the target address may not be equivalent to destroying the source address: for example, if the target address is a generic and the source address is AnyObject, specialization may turn the generic into a trivial type; the destruction of that trivial type fails to destroy the original stored AnyObject, resulting in a leak. --- .../Transforms/SSADestroyHoisting.cpp | 22 ++++++++-- test/SILOptimizer/hoist_destroy_addr.sil | 42 ++++++++++++++++++- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp index 42e44192c63ea..b0b7630887580 100644 --- a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp @@ -786,8 +786,15 @@ bool HoistDestroys::checkFoldingBarrier( SILValue address; if (auto *load = dyn_cast(instruction)) { auto loadee = load->getOperand(); - auto accessPath = AccessPath::compute(loadee); - if (accessPath.getStorage().hasIdenticalStorage(storage)) { + auto relativeAccessStorage = RelativeAccessStorageWithBase::compute(loadee); + if (relativeAccessStorage.getStorage().hasIdenticalStorage(storage)) { + // If the access path from the loaded address to its root storage involves + // a (layout non-equivalent) typecast--a load [take] of the casted address + // would not be equivalent to a load [copy] followed by a destroy_addr of + // the corresponding uncast projection--the truncated portion might have + // refcounted components. + if (relativeAccessStorage.cast == AccessStorageCast::Type) + return true; if (load->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) { address = loadee; loads.push_back(load); @@ -798,8 +805,15 @@ bool HoistDestroys::checkFoldingBarrier( } } else if (auto *copy = dyn_cast(instruction)) { auto source = copy->getSrc(); - auto accessPath = AccessPath::compute(source); - if (accessPath.getStorage().hasIdenticalStorage(storage)) { + auto relativeAccessStorage = RelativeAccessStorageWithBase::compute(source); + if (relativeAccessStorage.getStorage().hasIdenticalStorage(storage)) { + // If the access path from the copy_addr'd address to its root storage + // involves a (layout non-equivalent) typecast--a copy_addr [take] of the + // casted address would not be equivalent to a copy_addr followed by a + // destroy_addr of the corresponding uncast projection--the truncated + // portion might have refcounted components. + if (relativeAccessStorage.cast == AccessStorageCast::Type) + return true; address = source; copies.push_back(copy); } diff --git a/test/SILOptimizer/hoist_destroy_addr.sil b/test/SILOptimizer/hoist_destroy_addr.sil index 4c946e47c3639..3865e6405db6c 100644 --- a/test/SILOptimizer/hoist_destroy_addr.sil +++ b/test/SILOptimizer/hoist_destroy_addr.sil @@ -7,6 +7,8 @@ sil_stage canonical import Builtin +typealias AnyObject = Builtin.AnyObject + class X { } @@ -390,7 +392,8 @@ entry(%addr : $*X, %instance : @owned $X): // CHECK-LABEL: sil [ossa] @hoist_upto_address_to_pointer_use : {{.*}} { // CHECK: address_to_pointer // CHECK: pointer_to_address -// CHECK: load [take] +// CHECK: load [copy] +// CHECK: destroy_addr // CHECK: tuple // CHECK-LABEL: } // end sil function 'hoist_upto_address_to_pointer_use' sil [ossa] @hoist_upto_address_to_pointer_use : $@convention(thin) (@in X) -> (@owned X) { @@ -795,3 +798,40 @@ entry(%instance : @owned $(X, I)): %retval = tuple (%copy : $(X, I), %i_2 : $I) return %retval : $((X, I), I) } + +// Don't fold a destroy_addr of underlying storage into the copy_addr of a +// bitcast of that address. +// +// CHECK-LABEL: sil [ossa] @nofold_destroy_addr_original_into_copy_addr_cast : {{.*}} { +// CHECK: copy_addr {{%[^,]+}} +// CHECK: destroy_addr +// CHECK-LABEL: } // end sil function 'nofold_destroy_addr_original_into_copy_addr_cast' +sil [ossa] @nofold_destroy_addr_original_into_copy_addr_cast : $@convention(thin) (@owned AnyObject) -> @out T { +entry(%out : $*T, %instance : @owned $AnyObject): + %addr = alloc_stack $AnyObject + store %instance to [init] %addr : $*AnyObject + %cast_addr = unchecked_addr_cast %addr : $*AnyObject to $*T + copy_addr %cast_addr to [initialization] %out : $*T + destroy_addr %addr : $*AnyObject + dealloc_stack %addr : $*AnyObject + %retval = tuple () + return %retval : $() +} + +// Don't fold a destroy_addr of underlying storage into the load [copy] of a +// bitcast of that address. +// +// CHECK-LABEL: sil [ossa] @nofold_destroy_addr_original_into_load_cast : {{.*}} { +// CHECK: load [copy] +// CHECK: destroy_addr +// CHECK-LABEL: } // end sil function 'nofold_destroy_addr_original_into_load_cast' +sil [ossa] @nofold_destroy_addr_original_into_load_cast : $@convention(thin) (@owned AnyObject) -> @owned X { +entry(%instance : @owned $AnyObject): + %addr = alloc_stack $AnyObject + store %instance to [init] %addr : $*AnyObject + %cast_addr = unchecked_addr_cast %addr : $*AnyObject to $*X + %cast = load [copy] %cast_addr : $*X + destroy_addr %addr : $*AnyObject + dealloc_stack %addr : $*AnyObject + return %cast : $X +} From 696c8f1d9de184632bcc5e70c72a1067a8cde48c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 11:27:48 -0700 Subject: [PATCH 140/242] [ConstraintSystem] Implement simplication of `PatternMatch` element --- lib/Sema/ConstraintSystem.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 30e6315fb3747..6be6f7b3ac15b 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4758,6 +4758,12 @@ void constraints::simplifyLocator(ASTNode &anchor, path = path.slice(1); continue; } + + if (anchor.is()) { + path = path.slice(1); + continue; + } + break; case ConstraintLocator::SubscriptMember: @@ -4851,6 +4857,13 @@ void constraints::simplifyLocator(ASTNode &anchor, continue; } + case ConstraintLocator::PatternMatch: { + auto patternElt = path[0].castTo(); + anchor = patternElt.getPattern(); + path = path.slice(1); + continue; + } + default: // FIXME: Lots of other cases to handle. break; From fed7c691128314fb68433da12f6662fc54b8aa74 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 11:28:16 -0700 Subject: [PATCH 141/242] [ConstraintSystem] Implement simplication of `ClosureBodyElement` element --- lib/Sema/ConstraintSystem.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 6be6f7b3ac15b..7b19992952383 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4857,6 +4857,13 @@ void constraints::simplifyLocator(ASTNode &anchor, continue; } + case ConstraintLocator::ClosureBodyElement: { + auto bodyElt = path[0].castTo(); + anchor = bodyElt.getElement(); + path = path.slice(1); + continue; + } + case ConstraintLocator::PatternMatch: { auto patternElt = path[0].castTo(); anchor = patternElt.getPattern(); From bb432e80b27936993bc768e078259fb566f37334 Mon Sep 17 00:00:00 2001 From: Puyan Lotfi Date: Tue, 15 Mar 2022 19:18:26 -0400 Subject: [PATCH 142/242] [cxx-interop] Test for apinotes fix in llvm-project. This is the test that goes with https://github.com/apple/llvm-project/pull/4074 In the PR to apple/llvm-project we were bailing on DeclContexts that were not isFileContext == true. This is problematic because with C++-Interop enabled we can end up with a DeclContext that is an extern "C". This test makes sure that the APINote is applied and that the code from the 4074 PR no longer bails out. --- test/Interop/Cxx/apinotes/Inputs/SomeModule.apinotes | 5 +++++ test/Interop/Cxx/apinotes/Inputs/SomeModule.h | 4 ++++ test/Interop/Cxx/apinotes/Inputs/module.modulemap | 4 ++++ test/Interop/Cxx/apinotes/apinotes-objcxx-smoke.swift | 9 +++++++++ 4 files changed, 22 insertions(+) create mode 100644 test/Interop/Cxx/apinotes/Inputs/SomeModule.apinotes create mode 100644 test/Interop/Cxx/apinotes/Inputs/SomeModule.h create mode 100644 test/Interop/Cxx/apinotes/Inputs/module.modulemap create mode 100644 test/Interop/Cxx/apinotes/apinotes-objcxx-smoke.swift diff --git a/test/Interop/Cxx/apinotes/Inputs/SomeModule.apinotes b/test/Interop/Cxx/apinotes/Inputs/SomeModule.apinotes new file mode 100644 index 0000000000000..ebb6627cc1201 --- /dev/null +++ b/test/Interop/Cxx/apinotes/Inputs/SomeModule.apinotes @@ -0,0 +1,5 @@ +--- +Name: SomeModule +Classes: +- Name: NSSomeClass + SwiftName: SomeClass diff --git a/test/Interop/Cxx/apinotes/Inputs/SomeModule.h b/test/Interop/Cxx/apinotes/Inputs/SomeModule.h new file mode 100644 index 0000000000000..efd91cc0bb2ee --- /dev/null +++ b/test/Interop/Cxx/apinotes/Inputs/SomeModule.h @@ -0,0 +1,4 @@ +@interface NSSomeClass + -(instancetype)init; +@end + diff --git a/test/Interop/Cxx/apinotes/Inputs/module.modulemap b/test/Interop/Cxx/apinotes/Inputs/module.modulemap new file mode 100644 index 0000000000000..fd895cab670a1 --- /dev/null +++ b/test/Interop/Cxx/apinotes/Inputs/module.modulemap @@ -0,0 +1,4 @@ +module SomeModule [extern_c] { + requires objc + header "SomeModule.h" +} \ No newline at end of file diff --git a/test/Interop/Cxx/apinotes/apinotes-objcxx-smoke.swift b/test/Interop/Cxx/apinotes/apinotes-objcxx-smoke.swift new file mode 100644 index 0000000000000..7c0002b66f03d --- /dev/null +++ b/test/Interop/Cxx/apinotes/apinotes-objcxx-smoke.swift @@ -0,0 +1,9 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=SomeModule -I %S/Inputs -source-filename=x -enable-cxx-interop -enable-objc-interop | %FileCheck -check-prefix=CHECK-IDE-TEST %s +// RUN: %swift-frontend -c -enable-cxx-interop -enable-objc-interop -I %S/Inputs %s -o - -emit-sil | %FileCheck %s + +import SomeModule + +// CHECK: @objc @_inheritsConvenienceInitializers class MyClass : SomeClass +// CHECK-IDE-TEST: typealias NSSomeClass = SomeClass +// CHECK-IDE-TEST-NEXT: class SomeClass +class MyClass : SomeClass { } From 3b4c637b0a191f3d4611c8a745012e49234c74e4 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 11:37:20 -0700 Subject: [PATCH 143/242] [ConstraintSystem] Implement simplication of assorted `Type` elements --- lib/Sema/ConstraintSystem.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 7b19992952383..d9ec008c5f447 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4871,8 +4871,14 @@ void constraints::simplifyLocator(ASTNode &anchor, continue; } - default: - // FIXME: Lots of other cases to handle. + case ConstraintLocator::PackType: + case ConstraintLocator::ParentType: + case ConstraintLocator::KeyPathType: + case ConstraintLocator::InstanceType: + case ConstraintLocator::PlaceholderType: + case ConstraintLocator::SequenceElementType: + case ConstraintLocator::ConstructorMemberType: + case ConstraintLocator::ExistentialSuperclassType: break; } From 363954a4163d373bf5f39cf69e2fa4794f7ee0e0 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 16 Mar 2022 13:01:30 -0700 Subject: [PATCH 144/242] Put Variadic Generics Behind a Flag --- include/swift/Basic/LangOptions.h | 3 +++ include/swift/Option/FrontendOptions.td | 4 ++++ lib/Frontend/CompilerInvocation.cpp | 3 +++ lib/Parse/ParseDecl.cpp | 8 ++++++-- lib/Sema/CSApply.cpp | 3 ++- lib/Sema/CSSimplify.cpp | 3 ++- lib/Sema/TypeCheckType.cpp | 3 ++- test/Constraints/type_sequence.swift | 2 +- test/decl/func/vararg.swift | 4 ++-- 9 files changed, 25 insertions(+), 8 deletions(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index c73be27773a30..f21021982f1ad 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -352,6 +352,9 @@ namespace swift { /// Enable experimental pairwise `buildBlock` for result builders. bool EnableExperimentalPairwiseBuildBlock = false; + /// Enable variadic generics. + bool EnableExperimentalVariadicGenerics = false; + /// Disable the implicit import of the _Concurrency module. bool DisableImplicitConcurrencyModuleImport = !SWIFT_IMPLICIT_CONCURRENCY_IMPORT; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 778f562cff7c8..a773cbf9637c0 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -546,6 +546,10 @@ def enable_experimental_string_processing : Flag<["-"], "enable-experimental-string-processing">, HelpText<"Enable experimental string processing">; +def enable_experimental_variadic_generics : + Flag<["-"], "enable-experimental-variadic-generics">, + HelpText<"Enable experimental support for variadic generic types">; + def disable_availability_checking : Flag<["-"], "disable-availability-checking">, HelpText<"Disable checking for potentially unavailable APIs">; diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 9e9cddc9f2ae7..783eb5214d2ec 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -456,6 +456,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, OPT_disable_experimental_opened_existential_types, false); + Opts.EnableExperimentalVariadicGenerics |= + Args.hasArg(OPT_enable_experimental_variadic_generics); + // SwiftOnoneSupport produces different symbols when opening existentials, // so disable it. if (FrontendOpts.ModuleName == SWIFT_ONONE_SUPPORT) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7ee13b9211a58..14799a3e26414 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2802,8 +2802,12 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } case DAK_TypeSequence: { - auto range = SourceRange(Loc, Tok.getRange().getStart()); - Attributes.add(TypeSequenceAttr::create(Context, AtLoc, range)); + if (Context.LangOpts.EnableExperimentalVariadicGenerics) { + auto range = SourceRange(Loc, Tok.getRange().getStart()); + Attributes.add(TypeSequenceAttr::create(Context, AtLoc, range)); + } else { + DiscardAttribute = true; + } break; } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 0daf5a6756ea3..47248f59d8303 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5500,7 +5500,8 @@ ArgumentList *ExprRewriter::coerceCallArguments( auto paramLabel = param.getLabel(); // Handle variadic generic parameters. - if (paramInfo.isVariadicGenericParameter(paramIdx)) { + if (ctx.LangOpts.EnableExperimentalVariadicGenerics && + paramInfo.isVariadicGenericParameter(paramIdx)) { assert(param.isVariadic()); assert(!param.isInOut()); diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index b4809cbaecfbc..37f802a7c759c 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -1614,7 +1614,8 @@ static ConstraintSystem::TypeMatchResult matchCallArguments( // We pull these out special because variadic parameters ban lots of // the more interesting typing constructs called out below like // inout and @autoclosure. - if (paramInfo.isVariadicGenericParameter(paramIdx)) { + if (cs.getASTContext().LangOpts.EnableExperimentalVariadicGenerics && + paramInfo.isVariadicGenericParameter(paramIdx)) { auto *PET = paramTy->castTo(); OpenTypeSequenceElements openTypeSequence{cs, PET}; for (auto argIdx : parameterBindings[paramIdx]) { diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 0cb05b17edb99..81341c76a5d8a 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -3772,7 +3772,8 @@ NeverNullType TypeResolver::resolveTupleType(TupleTypeRepr *repr, bool complained = false; if (repr->hasEllipsis()) { - if (repr->getNumElements() == 1 && !repr->hasElementNames()) { + if (getASTContext().LangOpts.EnableExperimentalVariadicGenerics && + repr->getNumElements() == 1 && !repr->hasElementNames()) { // This is probably a pack expansion. Try to resolve the pattern type. auto patternTy = resolveType(repr->getElementType(0), elementOptions); if (patternTy->hasError()) diff --git a/test/Constraints/type_sequence.swift b/test/Constraints/type_sequence.swift index 4ec10db9ad91b..ad0ee71bf0b06 100644 --- a/test/Constraints/type_sequence.swift +++ b/test/Constraints/type_sequence.swift @@ -1,4 +1,4 @@ -// RUN: %target-typecheck-verify-swift +// RUN: %target-typecheck-verify-swift -enable-experimental-variadic-generics struct TupleStruct { var first: First diff --git a/test/decl/func/vararg.swift b/test/decl/func/vararg.swift index 506199f82b716..ea458d19043b1 100644 --- a/test/decl/func/vararg.swift +++ b/test/decl/func/vararg.swift @@ -1,7 +1,7 @@ // RUN: %target-typecheck-verify-swift -var t1a: (Int...) = (1) // expected-error{{cannot create expansion with non-variadic type 'Int'}} -// expected-error@-1 {{cannot convert value of type 'Int' to specified type '(Int...)'}} +var t1a: (Int...) = (1) +// expected-error@-1 {{cannot create a variadic tuple}} var t2d: (Double = 0.0) = 1 // expected-error {{default argument not permitted in a tuple type}} {{18-23=}} func f1(_ a: Int...) { for _ in a {} } From 3f47ed23651ed1beab61eb04490df93635fddb51 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 11:51:58 -0700 Subject: [PATCH 145/242] [ConstraintSystem] Implement simplication of `{Function, Generic, Synthesized}Argument` elements --- lib/Sema/ConstraintSystem.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index d9ec008c5f447..3f29245a478a2 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4880,6 +4880,11 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::ConstructorMemberType: case ConstraintLocator::ExistentialSuperclassType: break; + + case ConstraintLocator::GenericArgument: + case ConstraintLocator::FunctionArgument: + case ConstraintLocator::SynthesizedArgument: + break; } // If we get here, we couldn't simplify the path further. From 6f3203dba17bb082feb7ec13f0471bbd2ad40c48 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 11:53:41 -0700 Subject: [PATCH 146/242] [ConstraintSystem] Implement simplication of `{DynamicLookup, KeyPathComponent}Result` elements --- lib/Sema/ConstraintSystem.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 3f29245a478a2..b581f0e68c4c8 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4885,6 +4885,11 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::FunctionArgument: case ConstraintLocator::SynthesizedArgument: break; + + case ConstraintLocator::DynamicLookupResult: + case ConstraintLocator::KeyPathComponentResult: + break; + } // If we get here, we couldn't simplify the path further. From 9dbda8573cfc37c79df59101861df9a09e81d303 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 12:02:46 -0700 Subject: [PATCH 147/242] [ConstraintSystem] Implement simplication of `GenericParameter` element --- lib/Sema/ConstraintSystem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index b581f0e68c4c8..7d5c1239c397b 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4890,6 +4890,9 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::KeyPathComponentResult: break; + case ConstraintLocator::GenericParameter: + break; + } // If we get here, we couldn't simplify the path further. From 3d8333bd71db06af95406543d62f9bac277d6943 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 12:05:37 -0700 Subject: [PATCH 148/242] [ConstraintSystem] Implement simplication of `KeyPath{Root, Value}` elements --- lib/Sema/ConstraintSystem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 7d5c1239c397b..d5a76d9fb8427 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4893,6 +4893,9 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::GenericParameter: break; + case ConstraintLocator::KeyPathRoot: + case ConstraintLocator::KeyPathValue: + break; } // If we get here, we couldn't simplify the path further. From 46ecae37e9a2496b0c237072faf3e075ffaca675 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 12:08:27 -0700 Subject: [PATCH 149/242] [ConstraintSystem] Implement simplication of `Opened{Generic, OpaqueArchetype}` elements --- lib/Sema/ConstraintSystem.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index d5a76d9fb8427..355e8c910fc8a 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4893,6 +4893,10 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::GenericParameter: break; + case ConstraintLocator::OpenedGeneric: + case ConstraintLocator::OpenedOpaqueArchetype: + break; + case ConstraintLocator::KeyPathRoot: case ConstraintLocator::KeyPathValue: break; From 029b11234b90d115a42a71a73343b824a0adbf76 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 12:13:39 -0700 Subject: [PATCH 150/242] [ConstraintSystem] Implement simplication of generic requirement elements --- lib/Sema/ConstraintSystem.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 355e8c910fc8a..77ef2a5004485 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4900,6 +4900,12 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::KeyPathRoot: case ConstraintLocator::KeyPathValue: break; + + case ConstraintLocator::ProtocolRequirement: + case ConstraintLocator::ConditionalRequirement: + case ConstraintLocator::ConformanceRequirement: + case ConstraintLocator::TypeParameterRequirement: + break; } // If we get here, we couldn't simplify the path further. From 8fe78d485e575033723cf54087d1971cbe6840ec Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 12:32:57 -0700 Subject: [PATCH 151/242] [ConstraintSystem] Implement simplication of `{Pack, PatternBinding}Element` elements --- lib/Sema/ConstraintSystem.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 77ef2a5004485..c37e7056f3761 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4906,6 +4906,20 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::ConformanceRequirement: case ConstraintLocator::TypeParameterRequirement: break; + + case ConstraintLocator::PackElement: + break; + + case ConstraintLocator::PatternBindingElement: { + auto pattern = path[0].castTo(); + auto *patternBinding = cast(anchor.get()); + anchor = patternBinding->getInit(pattern.getIndex()); + // If this pattern is uninitialized, let's use it as anchor. + if (!anchor) + anchor = patternBinding->getPattern(pattern.getIndex()); + path = path.slice(1); + continue; + } } // If we get here, we couldn't simplify the path further. From 9bd05b7a00e73b66f7ec880be32f6df0757ddd40 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 12:35:57 -0700 Subject: [PATCH 152/242] [ConstraintSystem] Implement simplication of `ImplicitConversion` element --- lib/Sema/ConstraintSystem.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index c37e7056f3761..41969889553af 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4920,6 +4920,9 @@ void constraints::simplifyLocator(ASTNode &anchor, path = path.slice(1); continue; } + + case ConstraintLocator::ImplicitConversion: + break; } // If we get here, we couldn't simplify the path further. From 1505d7cc1495d5840804bb62fdf2f855e0874694 Mon Sep 17 00:00:00 2001 From: Luciano Almeida Date: Wed, 16 Mar 2022 18:31:50 -0300 Subject: [PATCH 153/242] [Diagnostic][Sema] Record locator with OptionalPayload dropped for contextual mismatch fixes for existentials --- lib/Sema/CSSimplify.cpp | 6 +++--- test/Constraints/protocols.swift | 12 ++++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index b4809cbaecfbc..953b0f55b11ef 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3407,7 +3407,7 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, if (last.is()) { auto *fix = AllowArgumentMismatch::create( - *this, type1, proto, getConstraintLocator(locator)); + *this, type1, proto, getConstraintLocator(anchor, path)); // Impact is 2 here because there are two failures // 1 - missing conformance and 2 - incorrect argument type. @@ -3443,14 +3443,14 @@ ConstraintSystem::matchExistentialTypes(Type type1, Type type2, if (isExpr(anchor)) { auto *fix = ContextualMismatch::create( - *this, type1, type2, getConstraintLocator(locator)); + *this, type1, type2, getConstraintLocator(anchor, path)); if (recordFix(fix)) return getTypeMatchFailure(locator); break; } auto *fix = MissingConformance::forContextual( - *this, type1, proto, getConstraintLocator(locator)); + *this, type1, proto, getConstraintLocator(anchor, path)); if (recordFix(fix)) return getTypeMatchFailure(locator); diff --git a/test/Constraints/protocols.swift b/test/Constraints/protocols.swift index 7e695d01b7339..386f7959bd408 100644 --- a/test/Constraints/protocols.swift +++ b/test/Constraints/protocols.swift @@ -519,3 +519,15 @@ case test(cond: Bool, v: Int64) } } } + +// SR-15970 +protocol SR15970_P {} +struct SR15970_S {} + +func SR15970_F(x: Int) -> SR15970_P { + return SR15970_S() // expected-error{{return expression of type 'SR15970_S' does not conform to 'SR15970_P'}} +} + +func SR15970_F1(x: Int) -> SR15970_P? { + return SR15970_S() // expected-error{{return expression of type 'SR15970_S' does not conform to 'SR15970_P'}} +} From 974fbc116795dc45c5cf0c3cb748ea2819ed5648 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 15 Mar 2022 14:03:56 -0700 Subject: [PATCH 154/242] [NFC] Move ObjC method support to NominalTypeDecl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares us to generalize ObjC selector collision diagnostics to also include protocols. NFC in this commit because, even though Sema and ClangImporter now try to record ObjC methods on non-`ClassDecl`s, `NominalTypeDecl::createObjCMethodLookup()` still doesn’t create ObjC method tables for them, so the calls are no-ops. --- include/swift/AST/ASTContext.h | 12 +++++--- include/swift/AST/Decl.h | 50 ++++++++++++++++---------------- include/swift/AST/SourceFile.h | 4 +-- lib/AST/ASTContext.cpp | 17 +++++++++-- lib/AST/NameLookup.cpp | 33 +++++++++++---------- lib/ClangImporter/ImportDecl.cpp | 6 ++-- lib/Sema/TypeCheckDeclObjC.cpp | 10 +++---- 7 files changed, 76 insertions(+), 56 deletions(-) diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index b97cf8e2fd9d1..1b68b07373189 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -988,11 +988,11 @@ class ASTContext final { /// one. void loadExtensions(NominalTypeDecl *nominal, unsigned previousGeneration); - /// Load the methods within the given class that produce + /// Load the methods within the given type that produce /// Objective-C class or instance methods with the given selector. /// - /// \param classDecl The class in which we are searching for @objc methods. - /// The search only considers this class and its extensions; not any + /// \param tyDecl The type in which we are searching for @objc methods. + /// The search only considers this type and its extensions; not any /// superclasses. /// /// \param selector The selector to search for. @@ -1010,7 +1010,11 @@ class ASTContext final { /// /// \param swiftOnly If true, only loads methods from imported Swift modules, /// skipping the Clang importer. - void loadObjCMethods(ClassDecl *classDecl, ObjCSelector selector, + /// + /// \note Passing a protocol is supported, but currently a no-op, because + /// Objective-C protocols cannot be extended in ways that make the ObjC method + /// lookup table relevant. + void loadObjCMethods(NominalTypeDecl *tyDecl, ObjCSelector selector, bool isInstanceMethod, unsigned previousGeneration, llvm::TinyPtrVector &methods, bool swiftOnly = false); diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 91ae1164268bb..2e2c25dbcb207 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3277,6 +3277,7 @@ class AssociatedTypeDecl : public AbstractTypeParamDecl { }; class MemberLookupTable; +class ObjCMethodLookupTable; class ConformanceLookupTable; // Kinds of pointer types. @@ -3382,6 +3383,12 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { getSatisfiedProtocolRequirementsForMember(const ValueDecl *Member, bool Sorted) const; + ObjCMethodLookupTable *ObjCMethodLookup = nullptr; + + /// Create the Objective-C method lookup table, or return \c false if this + /// kind of type cannot have Objective-C methods. + bool createObjCMethodLookup(); + friend class ASTContext; friend class MemberLookupTable; friend class ConformanceLookupTable; @@ -3531,6 +3538,24 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { void setConformanceLoader(LazyMemberLoader *resolver, uint64_t contextData); + /// Look in this type and its extensions (but not any of its protocols or + /// superclasses) for declarations with a given Objective-C selector. + /// + /// Note that this can find methods, initializers, deinitializers, + /// getters, and setters. + /// + /// \param selector The Objective-C selector of the method we're + /// looking for. + /// + /// \param isInstance Whether we are looking for an instance method + /// (vs. a class method). + TinyPtrVector lookupDirect(ObjCSelector selector, + bool isInstance); + + /// Record the presence of an @objc method with the given selector. No-op if + /// the type is of a kind which cannot contain @objc methods. + void recordObjCMethod(AbstractFunctionDecl *method, ObjCSelector selector); + /// Is this the decl for Optional? bool isOptionalDecl() const; @@ -3954,13 +3979,7 @@ using AncestryOptions = OptionSet; /// The type of the decl itself is a MetatypeType; use getDeclaredType() /// to get the declared type ("Complex" in the above example). class ClassDecl final : public NominalTypeDecl { - class ObjCMethodLookupTable; - SourceLoc ClassLoc; - ObjCMethodLookupTable *ObjCMethodLookup = nullptr; - - /// Create the Objective-C member lookup table. - void createObjCMethodLookup(); struct { /// The superclass decl and a bit to indicate whether the @@ -4225,25 +4244,6 @@ class ClassDecl final : public NominalTypeDecl { /// the Objective-C runtime. StringRef getObjCRuntimeName(llvm::SmallVectorImpl &buffer) const; - using NominalTypeDecl::lookupDirect; - - /// Look in this class and its extensions (but not any of its protocols or - /// superclasses) for declarations with a given Objective-C selector. - /// - /// Note that this can find methods, initializers, deinitializers, - /// getters, and setters. - /// - /// \param selector The Objective-C selector of the method we're - /// looking for. - /// - /// \param isInstance Whether we are looking for an instance method - /// (vs. a class method). - TinyPtrVector lookupDirect(ObjCSelector selector, - bool isInstance); - - /// Record the presence of an @objc method with the given selector. - void recordObjCMethod(AbstractFunctionDecl *method, ObjCSelector selector); - // Implement isa/cast/dyncast/etc. static bool classof(const Decl *D) { return D->getKind() == DeclKind::Class; diff --git a/include/swift/AST/SourceFile.h b/include/swift/AST/SourceFile.h index 1dd78a6f3b12a..3d073ed2e88c6 100644 --- a/include/swift/AST/SourceFile.h +++ b/include/swift/AST/SourceFile.h @@ -243,8 +243,8 @@ class SourceFile final : public FileUnit { std::vector ObjCUnsatisfiedOptReqs; /// A selector that is used by two different declarations in the same class. - /// Fields: classDecl, selector, isInstanceMethod. - using ObjCMethodConflict = std::tuple; + /// Fields: typeDecl, selector, isInstanceMethod. + using ObjCMethodConflict = std::tuple; /// List of Objective-C member conflicts we have found during type checking. std::vector ObjCMethodConflicts; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 946583a92ebda..e49f8750f4c64 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1906,11 +1906,24 @@ void ASTContext::loadExtensions(NominalTypeDecl *nominal, } void ASTContext::loadObjCMethods( - ClassDecl *classDecl, ObjCSelector selector, bool isInstanceMethod, + NominalTypeDecl *tyDecl, ObjCSelector selector, bool isInstanceMethod, unsigned previousGeneration, llvm::TinyPtrVector &methods, bool swiftOnly) { PrettyStackTraceSelector stackTraceSelector("looking for", selector); - PrettyStackTraceDecl stackTraceDecl("...in", classDecl); + PrettyStackTraceDecl stackTraceDecl("...in", tyDecl); + + // @objc protocols cannot have @objc extension members, so if we've recorded + // everything in the protocol definition, we've recorded everything. And we + // only ever use the ObjCSelector version of `NominalTypeDecl::lookupDirect()` + // on protocols in primary file typechecking, so we don't care about protocols + // that need to be loaded from files. + // TODO: Rework `ModuleLoader::loadObjCMethods()` to support protocols too if + // selector-based `NominalTypeDecl::lookupDirect()` ever needs to work + // in more situations. + ClassDecl *classDecl = dyn_cast(tyDecl); + if (!classDecl) + return; + for (auto &loader : getImpl().ModuleLoaders) { // Ignore the Clang importer if we've been asked for Swift-only results. if (swiftOnly && loader.get() == getClangModuleLoader()) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index d9dc9c7934776..d5f14fc85ec76 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1125,10 +1125,10 @@ namespace { /// Class member lookup table, which is a member lookup table with a second /// table for lookup based on Objective-C selector. -class ClassDecl::ObjCMethodLookupTable +class swift::ObjCMethodLookupTable : public llvm::DenseMap, StoredObjCMethods>, - public ASTAllocated + public ASTAllocated {}; MemberLookupTable::MemberLookupTable(ASTContext &ctx) { @@ -1483,23 +1483,27 @@ DirectLookupRequest::evaluate(Evaluator &evaluator, includeAttrImplements); } -void ClassDecl::createObjCMethodLookup() { +bool NominalTypeDecl::createObjCMethodLookup() { assert(!ObjCMethodLookup && "Already have an Objective-C member table"); + + // Most types cannot have ObjC methods. + if (!(isa(this))) + return false; + auto &ctx = getASTContext(); ObjCMethodLookup = new (ctx) ObjCMethodLookupTable(); // Register a cleanup with the ASTContext to call the lookup table // destructor. - ctx.addCleanup([this]() { - this->ObjCMethodLookup->~ObjCMethodLookupTable(); - }); + ctx.addDestructorCleanup(*ObjCMethodLookup); + + return true; } TinyPtrVector -ClassDecl::lookupDirect(ObjCSelector selector, bool isInstance) { - if (!ObjCMethodLookup) { - createObjCMethodLookup(); - } +NominalTypeDecl::lookupDirect(ObjCSelector selector, bool isInstance) { + if (!ObjCMethodLookup && !createObjCMethodLookup()) + return {}; // If any modules have been loaded since we did the search last (or if we // hadn't searched before), look in those modules, too. @@ -1514,11 +1518,10 @@ ClassDecl::lookupDirect(ObjCSelector selector, bool isInstance) { return stored.Methods; } -void ClassDecl::recordObjCMethod(AbstractFunctionDecl *method, - ObjCSelector selector) { - if (!ObjCMethodLookup) { - createObjCMethodLookup(); - } +void NominalTypeDecl::recordObjCMethod(AbstractFunctionDecl *method, + ObjCSelector selector) { + if (!ObjCMethodLookup && !createObjCMethodLookup()) + return; // Record the method. bool isInstanceMethod = method->isObjCInstanceMethod(); diff --git a/lib/ClangImporter/ImportDecl.cpp b/lib/ClangImporter/ImportDecl.cpp index 68efb9405a138..0b6613fda8826 100644 --- a/lib/ClangImporter/ImportDecl.cpp +++ b/lib/ClangImporter/ImportDecl.cpp @@ -4815,12 +4815,12 @@ namespace { decl->setIsDynamic(true); // If the declaration we attached the 'objc' attribute to is within a - // class, record it in the class. + // type, record it in the type. if (auto contextTy = decl->getDeclContext()->getDeclaredInterfaceType()) { - if (auto classDecl = contextTy->getClassOrBoundGenericClass()) { + if (auto tyDecl = contextTy->getNominalOrBoundGenericNominal()) { if (auto method = dyn_cast(decl)) { if (name) - classDecl->recordObjCMethod(method, *name); + tyDecl->recordObjCMethod(method, *name); } } } diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index b57161c132197..9b520869f69c8 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -2008,9 +2008,9 @@ void markAsObjC(ValueDecl *D, ObjCReason reason, } } - // Record the method in the class, if it's a member of one. - if (auto classDecl = D->getDeclContext()->getSelfClassDecl()) { - classDecl->recordObjCMethod(method, selector); + // Record the method in the type, if it's a member of one. + if (auto tyDecl = D->getDeclContext()->getSelfNominalTypeDecl()) { + tyDecl->recordObjCMethod(method, selector); } // Record the method in the source file. @@ -2406,11 +2406,11 @@ bool swift::diagnoseUnintendedObjCMethodOverrides(SourceFile &sf) { /// Retrieve the source file for the given Objective-C member conflict. static TinyPtrVector getObjCMethodConflictDecls(const SourceFile::ObjCMethodConflict &conflict) { - ClassDecl *classDecl = std::get<0>(conflict); + NominalTypeDecl *typeDecl = std::get<0>(conflict); ObjCSelector selector = std::get<1>(conflict); bool isInstanceMethod = std::get<2>(conflict); - return classDecl->lookupDirect(selector, isInstanceMethod); + return typeDecl->lookupDirect(selector, isInstanceMethod); } static ObjCAttr *getObjCAttrIfFromAccessNote(ValueDecl *VD) { From 06949a4f4a0ed4b1e93381056512850cd93c4093 Mon Sep 17 00:00:00 2001 From: Becca Royal-Gordon Date: Tue, 15 Mar 2022 14:54:48 -0700 Subject: [PATCH 155/242] Check protocols for selector conflicts Although we have always checked classes to see if their @objc members had the same selectors, it turns out we never did this for protocols. Oops. Keep a table of ObjC selector names for protocols, just as we do for classes, and diagnose any conflicts between them. Fixes rdar://80990066. --- lib/AST/NameLookup.cpp | 2 +- lib/Sema/TypeCheckDeclObjC.cpp | 4 ++++ test/ClangImporter/objc_bridging_custom.swift | 3 ++- test/Constraints/common_type_objc.swift | 8 ++++---- test/Constraints/overload_filtering_objc.swift | 4 ++-- test/attr/attr_objc.swift | 2 ++ test/decl/protocol/objc.swift | 7 +++++++ 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index d5f14fc85ec76..2060cb9e254f5 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -1487,7 +1487,7 @@ bool NominalTypeDecl::createObjCMethodLookup() { assert(!ObjCMethodLookup && "Already have an Objective-C member table"); // Most types cannot have ObjC methods. - if (!(isa(this))) + if (!(isa(this) || isa(this))) return false; auto &ctx = getASTContext(); diff --git a/lib/Sema/TypeCheckDeclObjC.cpp b/lib/Sema/TypeCheckDeclObjC.cpp index 9b520869f69c8..e440ab9be37f8 100644 --- a/lib/Sema/TypeCheckDeclObjC.cpp +++ b/lib/Sema/TypeCheckDeclObjC.cpp @@ -2447,6 +2447,7 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) { // Diagnose each conflict. bool anyConflicts = false; for (const auto &conflict : localConflicts) { + NominalTypeDecl *tyDecl = std::get<0>(conflict); ObjCSelector selector = std::get<1>(conflict); auto methods = getObjCMethodConflictDecls(conflict); @@ -2530,6 +2531,9 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) { origDiagInfo.first, origDiagInfo.second, selector); + // Protocols weren't checked for selector conflicts in 5.0. + diag.warnUntilSwiftVersionIf(!isa(tyDecl), 6); + auto objcAttr = getObjCAttrIfFromAccessNote(conflictingDecl); swift::softenIfAccessNote(conflictingDecl, objcAttr, diag); if (objcAttr) diff --git a/test/ClangImporter/objc_bridging_custom.swift b/test/ClangImporter/objc_bridging_custom.swift index f7f193be475e5..b3a6c71cb288e 100644 --- a/test/ClangImporter/objc_bridging_custom.swift +++ b/test/ClangImporter/objc_bridging_custom.swift @@ -159,8 +159,9 @@ protocol TestProto { @objc optional func testUnmigrated(a: NSRuncingMode, b: Refrigerator, c: NSCoding) // expected-note {{here}} {{none}} @objc optional func testPartialMigrated(a: NSRuncingMode, b: Refrigerator) // expected-note {{here}} {{none}} - @objc optional subscript(a a: Refrigerator) -> Refrigerator? { get } // expected-note {{here}} {{none}} + @objc optional subscript(a a: Refrigerator) -> Refrigerator? { get } // expected-note 2 {{here}} {{none}} @objc optional subscript(generic a: ManufacturerInfo) -> ManufacturerInfo? { get } // expected-note {{here}} {{none}} + // expected-warning@-1 {{subscript getter with Objective-C selector 'objectForKeyedSubscript:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}} @objc optional var prop: Refrigerator? { get } // expected-note {{here}} {{none}} @objc optional var propGeneric: ManufacturerInfo? { get } // expected-note {{here}} {{none}} diff --git a/test/Constraints/common_type_objc.swift b/test/Constraints/common_type_objc.swift index 35a7239ae08cf..871834b63f39e 100644 --- a/test/Constraints/common_type_objc.swift +++ b/test/Constraints/common_type_objc.swift @@ -6,11 +6,11 @@ import Foundation @objc protocol P { - func foo(_ i: Int) -> Double - func foo(_ d: Double) -> Double + func foo(_ i: Int) -> Double // expected-note {{'foo' previously declared here}} + func foo(_ d: Double) -> Double // expected-warning {{method 'foo' with Objective-C selector 'foo:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}} - @objc optional func opt(_ i: Int) -> Int - @objc optional func opt(_ d: Double) -> Int + @objc optional func opt(_ i: Int) -> Int // expected-note {{'opt' previously declared here}} + @objc optional func opt(_ d: Double) -> Int // expected-warning {{method 'opt' with Objective-C selector 'opt:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}} } func testOptional(obj: P) { diff --git a/test/Constraints/overload_filtering_objc.swift b/test/Constraints/overload_filtering_objc.swift index d84d548a9ddd1..6ccad4d1b184c 100644 --- a/test/Constraints/overload_filtering_objc.swift +++ b/test/Constraints/overload_filtering_objc.swift @@ -9,8 +9,8 @@ import Foundation @objc protocol P { - func foo(_ i: Int) -> Int - func foo(_ d: Double) -> Int + func foo(_ i: Int) -> Int // expected-note {{'foo' previously declared here}} + func foo(_ d: Double) -> Int // expected-warning {{method 'foo' with Objective-C selector 'foo:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}} @objc optional func opt(_ i: Int) -> Int @objc optional func opt(double: Double) -> Int diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index a0125eca80c08..ae410c96b7d4f 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -364,9 +364,11 @@ protocol subject_containerObjCProtocol1 { @objc // access-note-move{{subject_containerObjCProtocol2}} protocol subject_containerObjCProtocol2 { init(a: Int) + // expected-note@-1 {{'init' previously declared here}} @objc // FIXME: Access notes can't distinguish between init(a:) overloads init(a: Double) + // expected-warning@-1 {{initializer 'init(a:)' with Objective-C selector 'initWithA:' conflicts with previous declaration with the same Objective-C selector; this is an error in Swift 6}} func func1() -> Int @objc // access-note-move{{subject_containerObjCProtocol2.func1_()}} diff --git a/test/decl/protocol/objc.swift b/test/decl/protocol/objc.swift index 0f6178643d9c4..f30a405d53392 100644 --- a/test/decl/protocol/objc.swift +++ b/test/decl/protocol/objc.swift @@ -258,3 +258,10 @@ class C8SubRW: C8Base { class C8SubRW2: C8Base { var prop: Int = 0 } + +@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) +@objc protocol P9 { + @objc(custom:) func f(_: Any) // expected-note 2 {{method 'f' declared here}} + @objc(custom:) func g(_: Any) // expected-warning {{method 'g' with Objective-C selector 'custom:' conflicts with method 'f' with the same Objective-C selector; this is an error in Swift 6}} + @objc(custom:) func h() async // expected-warning {{method 'h()' with Objective-C selector 'custom:' conflicts with method 'f' with the same Objective-C selector; this is an error in Swift 6}} +} From 135c266115b648fb834facd975fce005fc950d06 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Wed, 16 Mar 2022 23:09:58 +0100 Subject: [PATCH 156/242] [CodeCompletion] Call sawSolution when type checking a result builder --- include/swift/Sema/CompletionContextFinder.h | 7 ++- lib/Sema/BuilderTransform.cpp | 12 +++- lib/Sema/TypeCheckCodeCompletion.cpp | 59 +++++++++++--------- lib/Sema/TypeChecker.h | 11 +++- 4 files changed, 56 insertions(+), 33 deletions(-) diff --git a/include/swift/Sema/CompletionContextFinder.h b/include/swift/Sema/CompletionContextFinder.h index 123ee6cdcc893..803f14b37b185 100644 --- a/include/swift/Sema/CompletionContextFinder.h +++ b/include/swift/Sema/CompletionContextFinder.h @@ -13,6 +13,7 @@ #ifndef SWIFT_SEMA_COMPLETIONCONTEXTFINDER_H #define SWIFT_SEMA_COMPLETIONCONTEXTFINDER_H +#include "swift/AST/ASTNode.h" #include "swift/AST/ASTWalker.h" #include "swift/AST/Expr.h" #include "swift/Sema/CodeCompletionTypeChecking.h" @@ -49,10 +50,10 @@ class CompletionContextFinder : public ASTWalker { public: /// Finder for completion contexts within the provided initial expression. - CompletionContextFinder(Expr *initialExpr, DeclContext *DC) - : InitialExpr(initialExpr), InitialDC(DC) { + CompletionContextFinder(ASTNode initialNode, DeclContext *DC) + : InitialExpr(initialNode.dyn_cast()), InitialDC(DC) { assert(DC); - initialExpr->walk(*this); + initialNode.walk(*this); }; /// Finder for completion contexts within the outermost non-closure context of diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index a0503dd8c1ad6..301361498684d 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1736,7 +1736,17 @@ Optional TypeChecker::applyResultBuilderBodyTransform( // Solve the constraint system. SmallVector solutions; - if (cs.solve(solutions) || solutions.size() != 1) { + bool solvingFailed = cs.solve(solutions); + + if (cs.getASTContext().CompletionCallback) { + CompletionContextFinder analyzer(func, func->getDeclContext()); + filterSolutionsForCodeCompletion(solutions, analyzer); + for (const auto &solution : solutions) { + cs.getASTContext().CompletionCallback->sawSolution(solution); + } + } + + if (solvingFailed || solutions.size() != 1) { // Try to fix the system or provide a decent diagnostic. auto salvagedResult = cs.salvage(); switch (salvagedResult.getKind()) { diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 6f8ebddc4fef1..8c44b905c3c25 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -513,11 +513,32 @@ TypeChecker::getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, } } -/// Remove any solutions from the provided vector that both require fixes and -/// have a score worse than the best. -static void filterSolutions(SolutionApplicationTarget &target, - SmallVectorImpl &solutions, - CodeCompletionExpr *completionExpr) { +static bool hasTypeForCompletion(Solution &solution, + CompletionContextFinder &contextAnalyzer) { + if (contextAnalyzer.hasCompletionExpr()) { + return solution.hasType(contextAnalyzer.getCompletionExpr()); + } else { + assert(contextAnalyzer.hasCompletionKeyPathComponent()); + return solution.hasType( + contextAnalyzer.getKeyPathContainingCompletionComponent(), + contextAnalyzer.getKeyPathCompletionComponentIndex()); + } +} + +void TypeChecker::filterSolutionsForCodeCompletion( + SmallVectorImpl &solutions, + CompletionContextFinder &contextAnalyzer) { + // Ignore solutions that didn't end up involving the completion (e.g. due to + // a fix to skip over/ignore it). + llvm::erase_if(solutions, [&](Solution &S) { + if (hasTypeForCompletion(S, contextAnalyzer)) + return false; + // FIXME: Technically this should never happen, but it currently does in + // result builder contexts. Re-evaluate if we can assert here when we have + // multi-statement closure checking for result builders. + return true; + }); + if (solutions.size() <= 1) return; @@ -608,15 +629,6 @@ bool TypeChecker::typeCheckForCodeCompletion( if (!cs.solveForCodeCompletion(target, solutions)) return CompletionResult::Fallback; - // FIXME: instead of filtering, expose the score and viability to clients. - // Remove any solutions that both require fixes and have a score that is - // worse than the best. - CodeCompletionExpr *completionExpr = nullptr; - if (contextAnalyzer.hasCompletionExpr()) { - completionExpr = contextAnalyzer.getCompletionExpr(); - } - filterSolutions(target, solutions, completionExpr); - // Similarly, if the type-check didn't produce any solutions, fall back // to type-checking a sub-expression in isolation. if (solutions.empty()) @@ -626,19 +638,7 @@ bool TypeChecker::typeCheckForCodeCompletion( // closure body it could either be type-checked together with the context // or not, it's impossible to say without checking. if (contextAnalyzer.locatedInMultiStmtClosure()) { - auto &solution = solutions.front(); - - bool HasTypeForCompletionNode = false; - if (completionExpr) { - HasTypeForCompletionNode = solution.hasType(completionExpr); - } else { - assert(contextAnalyzer.hasCompletionKeyPathComponent()); - HasTypeForCompletionNode = solution.hasType( - contextAnalyzer.getKeyPathContainingCompletionComponent(), - contextAnalyzer.getKeyPathCompletionComponentIndex()); - } - - if (!HasTypeForCompletionNode) { + if (!hasTypeForCompletion(solutions.front(), contextAnalyzer)) { // At this point we know the code completion node wasn't checked with // the closure's surrounding context, so can defer to regular // type-checking for the current call to typeCheckExpression. If that @@ -651,6 +651,11 @@ bool TypeChecker::typeCheckForCodeCompletion( } } + // FIXME: instead of filtering, expose the score and viability to clients. + // Remove solutions that skipped over/ignored the code completion point + // or that require fixes and have a score that is worse than the best. + filterSolutionsForCodeCompletion(solutions, contextAnalyzer); + llvm::for_each(solutions, callback); return CompletionResult::Ok; }; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 1a57ee612dff2..b81ce52d22096 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -29,10 +29,11 @@ #include "swift/AST/NameLookup.h" #include "swift/AST/PropertyWrappers.h" #include "swift/AST/TypeRefinementContext.h" -#include "swift/Parse/Lexer.h" #include "swift/Basic/OptionSet.h" -#include "swift/Sema/ConstraintSystem.h" #include "swift/Config.h" +#include "swift/Parse/Lexer.h" +#include "swift/Sema/CompletionContextFinder.h" +#include "swift/Sema/ConstraintSystem.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/TinyPtrVector.h" #include @@ -571,6 +572,12 @@ FunctionType *getTypeOfCompletionOperator(DeclContext *DC, Expr *LHS, DeclRefKind refKind, ConcreteDeclRef &refdDecl); +/// Remove any solutions from the provided vector that require more fixes than +/// the best score or don't contain a type for the code completion token. +void filterSolutionsForCodeCompletion( + SmallVectorImpl &solutions, + CompletionContextFinder &contextAnalyzer); + /// Type check the given expression and provide results back to code completion /// via specified callback. /// From 57518b5894d4d468b285780d24395bd0e7542452 Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Wed, 16 Mar 2022 12:12:46 -0700 Subject: [PATCH 157/242] Do not escape UNICODE when writing out target info. Escaping unicode characters results in invalid JSON. - Refactor writeEscaped routine into StringExtras Resolves rdar://90108531 --- include/swift/Basic/StringExtras.h | 4 +++ lib/Basic/StringExtras.cpp | 25 +++++++++++++++++++ lib/Basic/TargetInfo.cpp | 22 ++++++++-------- test/Driver/print_target_info_unicode.swift | 5 ++++ .../sourcekitd/include/sourcekitd/Internal.h | 2 -- .../sourcekitd/RequestResponsePrinterBase.h | 3 ++- .../lib/API/sourcekitdAPI-Common.cpp | 25 +------------------ .../lib/API/sourcekitdAPI-InProc.cpp | 3 ++- 8 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 test/Driver/print_target_info_unicode.swift diff --git a/include/swift/Basic/StringExtras.h b/include/swift/Basic/StringExtras.h index f5a522c24d1f3..211ea71c801c1 100644 --- a/include/swift/Basic/StringExtras.h +++ b/include/swift/Basic/StringExtras.h @@ -515,6 +515,10 @@ class NullTerminatedStringRef { int compare(NullTerminatedStringRef RHS) const { return Ref.compare(RHS); } }; +/// A variant of write_escaped that does not escape Unicode characters - useful for generating JSON, +/// where escaped Unicode characters lead to malformed/invalid JSON. +void writeEscaped(llvm::StringRef Str, llvm::raw_ostream &OS); + } // end namespace swift #endif // SWIFT_BASIC_STRINGEXTRAS_H diff --git a/lib/Basic/StringExtras.cpp b/lib/Basic/StringExtras.cpp index ec0ad064f2cd0..f88dac9e62d6e 100644 --- a/lib/Basic/StringExtras.cpp +++ b/lib/Basic/StringExtras.cpp @@ -23,6 +23,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/raw_ostream.h" #include using namespace swift; @@ -1392,3 +1393,27 @@ Optional swift::stripWithCompletionHandlerSuffix(StringRef name) { return None; } + +void swift::writeEscaped(llvm::StringRef Str, llvm::raw_ostream &OS) { + for (unsigned i = 0, e = Str.size(); i != e; ++i) { + unsigned char c = Str[i]; + + switch (c) { + case '\\': + OS << '\\' << '\\'; + break; + case '\t': + OS << '\\' << 't'; + break; + case '\n': + OS << '\\' << 'n'; + break; + case '"': + OS << '\\' << '"'; + break; + default: + OS << c; + break; + } + } +} diff --git a/lib/Basic/TargetInfo.cpp b/lib/Basic/TargetInfo.cpp index 03c9bef17b778..1503fe0246cee 100644 --- a/lib/Basic/TargetInfo.cpp +++ b/lib/Basic/TargetInfo.cpp @@ -13,6 +13,7 @@ #include "swift/Basic/TargetInfo.h" #include "swift/Basic/Version.h" #include "swift/Basic/Platform.h" +#include "swift/Basic/StringExtras.h" #include "swift/Frontend/Frontend.h" #include "llvm/Support/raw_ostream.h" @@ -35,11 +36,11 @@ static void printCompatibilityLibrary( out << " {\n"; out << " \"libraryName\": \""; - out.write_escaped(libraryName); + swift::writeEscaped(libraryName, out); out << "\",\n"; out << " \"filter\": \""; - out.write_escaped(filter); + swift::writeEscaped(filter, out); out << "\"\n"; out << " }"; @@ -53,8 +54,7 @@ void targetinfo::printTargetInfo(const CompilerInvocation &invocation, // Compiler version, as produced by --version. out << " \"compilerVersion\": \""; - out.write_escaped(version::getSwiftFullVersion( - version::Version::getCurrentLanguageVersion())); + writeEscaped(version::getSwiftFullVersion(version::Version::getCurrentLanguageVersion()), out); out << "\",\n"; // Target triple and target variant triple. @@ -77,7 +77,7 @@ void targetinfo::printTargetInfo(const CompilerInvocation &invocation, if (!searchOpts.getSDKPath().empty()) { out << " \"sdkPath\": \""; - out.write_escaped(searchOpts.getSDKPath()); + writeEscaped(searchOpts.getSDKPath(), out); out << "\",\n"; } @@ -85,7 +85,7 @@ void targetinfo::printTargetInfo(const CompilerInvocation &invocation, out << " \"" << name << "\": [\n"; llvm::interleave(paths, [&out](const std::string &path) { out << " \""; - out.write_escaped(path); + writeEscaped(path, out); out << "\""; }, [&out] { out << ",\n"; @@ -98,7 +98,7 @@ void targetinfo::printTargetInfo(const CompilerInvocation &invocation, searchOpts.getRuntimeLibraryImportPaths()); out << " \"runtimeResourcePath\": \""; - out.write_escaped(searchOpts.RuntimeResourcePath); + writeEscaped(searchOpts.RuntimeResourcePath, out); out << "\"\n"; out << " }\n"; @@ -113,20 +113,20 @@ void targetinfo::printTripleInfo(const llvm::Triple &triple, out << "{\n"; out << " \"triple\": \""; - out.write_escaped(triple.getTriple()); + writeEscaped(triple.getTriple(), out); out << "\",\n"; out << " \"unversionedTriple\": \""; - out.write_escaped(getUnversionedTriple(triple).getTriple()); + writeEscaped(getUnversionedTriple(triple).getTriple(), out); out << "\",\n"; out << " \"moduleTriple\": \""; - out.write_escaped(getTargetSpecificModuleTriple(triple).getTriple()); + writeEscaped(getTargetSpecificModuleTriple(triple).getTriple(), out); out << "\",\n"; if (runtimeVersion) { out << " \"swiftRuntimeCompatibilityVersion\": \""; - out.write_escaped(runtimeVersion->getAsString()); + writeEscaped(runtimeVersion->getAsString(), out); out << "\",\n"; // Compatibility libraries that need to be linked. diff --git a/test/Driver/print_target_info_unicode.swift b/test/Driver/print_target_info_unicode.swift new file mode 100644 index 0000000000000..bf8147fcda109 --- /dev/null +++ b/test/Driver/print_target_info_unicode.swift @@ -0,0 +1,5 @@ +// RUN: %target-swift-frontend -print-target-info -target arm64-apple-ios12.0 -sdk /Applications🙉/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk | %FileCheck %s + +// CHECK: "target": { +// CHECK-NEXT: "triple": "arm64-apple-ios12.0", +// CHECK: "sdkPath": "/Applications🙉/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk", diff --git a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Internal.h b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Internal.h index 3c84059309a0b..2a81b1b9bd5c2 100644 --- a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Internal.h +++ b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/Internal.h @@ -194,8 +194,6 @@ sourcekitd_response_t createErrorRequestCancelled(); sourcekitd_uid_t SKDUIDFromUIdent(SourceKit::UIdent UID); SourceKit::UIdent UIdentFromSKDUID(sourcekitd_uid_t uid); -void writeEscaped(llvm::StringRef Str, llvm::raw_ostream &OS); - static inline sourcekitd_variant_t makeNullVariant() { return {{ 0, 0, 0 }}; } diff --git a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/RequestResponsePrinterBase.h b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/RequestResponsePrinterBase.h index 079c4dd8dc36e..ebdf8a6ade1bc 100644 --- a/tools/SourceKit/tools/sourcekitd/include/sourcekitd/RequestResponsePrinterBase.h +++ b/tools/SourceKit/tools/sourcekitd/include/sourcekitd/RequestResponsePrinterBase.h @@ -15,6 +15,7 @@ #include "sourcekitd/sourcekitd.h" #include "sourcekitd/Logging.h" +#include "swift/Basic/StringExtras.h" #include namespace llvm { @@ -92,7 +93,7 @@ class RequestResponsePrinterBase { OS << '\"'; // Avoid raw_ostream's write_escaped, we don't want to escape unicode // characters because it will be invalid JSON. - writeEscaped(Str, OS); + swift::writeEscaped(Str, OS); OS << '\"'; } diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-Common.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-Common.cpp index 4ea0728fbcab0..4c82695a21b55 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-Common.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-Common.cpp @@ -16,6 +16,7 @@ #include "sourcekitd/RequestResponsePrinterBase.h" #include "SourceKit/Support/Logging.h" #include "SourceKit/Support/UIdent.h" +#include "swift/Basic/StringExtras.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/ErrorHandling.h" @@ -133,30 +134,6 @@ class VariantPrinter : public VariantVisitor, }; } // end anonymous namespace -void sourcekitd::writeEscaped(llvm::StringRef Str, llvm::raw_ostream &OS) { - for (unsigned i = 0, e = Str.size(); i != e; ++i) { - unsigned char c = Str[i]; - - switch (c) { - case '\\': - OS << '\\' << '\\'; - break; - case '\t': - OS << '\\' << 't'; - break; - case '\n': - OS << '\\' << 'n'; - break; - case '"': - OS << '\\' << '"'; - break; - default: - OS << c; - break; - } - } -} - static void printError(sourcekitd_response_t Err, raw_ostream &OS) { OS << "error response ("; switch (sourcekitd_response_error_get_kind(Err)) { diff --git a/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-InProc.cpp b/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-InProc.cpp index 697d7eb849a7a..872f232da1d98 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-InProc.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/API/sourcekitdAPI-InProc.cpp @@ -23,6 +23,7 @@ #include "SourceKit/Core/LLVM.h" #include "SourceKit/Support/UIdent.h" #include "swift/Basic/ThreadSafeRefCounted.h" +#include "swift/Basic/StringExtras.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" @@ -405,7 +406,7 @@ class SKDObjectPrinter : public SKDObjectVisitor { OS << '\"'; // Avoid raw_ostream's write_escaped, we don't want to escape unicode // characters because it will be invalid JSON. - writeEscaped(Str, OS); + swift::writeEscaped(Str, OS); OS << '\"'; } From cc0bc22dcbf916c7c20f3e99966922b0b0dddae0 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 16 Mar 2022 17:28:45 -0700 Subject: [PATCH 158/242] Correct The Parsing of Primary Associated Type Clauses in Protocols The prior syntax tree did not take into account that the clause itself should own the angle brackets. --- include/swift/Parse/Parser.h | 2 + lib/Parse/ParseDecl.cpp | 34 +++++++++------ unittests/Syntax/DeclSyntaxTests.cpp | 41 +++++++++++++++++++ utils/gyb_syntax_support/DeclNodes.py | 3 ++ utils/gyb_syntax_support/GenericNodes.py | 9 ++++ .../NodeSerializationCodes.py | 1 + 6 files changed, 78 insertions(+), 12 deletions(-) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 660cde62a0d9e..8bea18fbd839b 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -1194,6 +1194,8 @@ class Parser { ParserStatus parsePrimaryAssociatedTypes( SmallVectorImpl &AssocTypes); + ParserStatus parsePrimaryAssociatedTypeList( + SmallVectorImpl &AssocTypes); ParserResult parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 7ee13b9211a58..d18e763786cef 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -7905,8 +7905,30 @@ ParserResult Parser::parseDeclClass(ParseDeclOptions Flags, ParserStatus Parser::parsePrimaryAssociatedTypes( SmallVectorImpl &AssocTypes) { + SyntaxParsingContext GPSContext(SyntaxContext, + SyntaxKind::PrimaryAssociatedTypeClause); + SourceLoc LAngleLoc = consumeStartingLess(); + auto Result = parsePrimaryAssociatedTypeList(AssocTypes); + + // Parse the closing '>'. + SourceLoc RAngleLoc; + if (startsWithGreater(Tok)) { + RAngleLoc = consumeStartingGreater(); + } else { + diagnose(Tok, diag::expected_rangle_primary_associated_type_list); + diagnose(LAngleLoc, diag::opening_angle); + + // Skip until we hit the '>'. + RAngleLoc = skipUntilGreaterInTypeList(); + } + + return Result; +} + +ParserStatus Parser::parsePrimaryAssociatedTypeList( + SmallVectorImpl &AssocTypes) { ParserStatus Result; SyntaxParsingContext PATContext(SyntaxContext, SyntaxKind::PrimaryAssociatedTypeList); @@ -7988,18 +8010,6 @@ ParserStatus Parser::parsePrimaryAssociatedTypes( HasNextParam = consumeIf(tok::comma); } while (HasNextParam); - // Parse the closing '>'. - SourceLoc RAngleLoc; - if (startsWithGreater(Tok)) { - RAngleLoc = consumeStartingGreater(); - } else { - diagnose(Tok, diag::expected_rangle_primary_associated_type_list); - diagnose(LAngleLoc, diag::opening_angle); - - // Skip until we hit the '>'. - RAngleLoc = skipUntilGreaterInTypeList(); - } - return Result; } diff --git a/unittests/Syntax/DeclSyntaxTests.cpp b/unittests/Syntax/DeclSyntaxTests.cpp index de61c50ac8a37..948d98b6eb199 100644 --- a/unittests/Syntax/DeclSyntaxTests.cpp +++ b/unittests/Syntax/DeclSyntaxTests.cpp @@ -633,3 +633,44 @@ TEST(DeclSyntaxTests, FunctionDeclWithAPIs) { TEST(DeclSyntaxTests, FunctionDeclBuilderAPIs) { } + +#pragma mark - parameterized protocol-decl + +TEST(DeclSyntaxTests, ProtocolMakeAPIs) { + RC Arena = SyntaxArena::make(); + SyntaxFactory Factory(Arena); + { + SmallString<1> Scratch; + llvm::raw_svector_ostream OS(Scratch); + Factory.makeBlankProtocolDecl().print(OS); + ASSERT_EQ(OS.str().str(), ""); + } + { + SmallString<64> Scratch; + llvm::raw_svector_ostream OS(Scratch); + auto Protocol = Factory.makeProtocolKeyword("", " "); + auto MyCollection = Factory.makeIdentifier("MyCollection", "", ""); + auto ElementName = Factory.makeIdentifier("Element", "", ""); + auto ElementParam = + Factory.makePrimaryAssociatedType(None, ElementName, None, None, None, None); + auto LeftAngle = Factory.makeLeftAngleToken("", ""); + auto RightAngle = Factory.makeRightAngleToken("", " "); + auto PrimaryAssocs = PrimaryAssociatedTypeClauseSyntaxBuilder(Arena) + .useLeftAngleBracket(LeftAngle) + .useRightAngleBracket(RightAngle) + .addPrimaryAssociatedType(ElementParam) + .build(); + + auto LeftBrace = Factory.makeLeftBraceToken("", ""); + auto RightBrace = Factory.makeRightBraceToken("", ""); + auto Members = MemberDeclBlockSyntaxBuilder(Arena) + .useLeftBrace(LeftBrace) + .useRightBrace(RightBrace) + .build(); + Factory + .makeProtocolDecl(None, None, Protocol, MyCollection, PrimaryAssocs, None, None, Members) + .print(OS); + ASSERT_EQ(OS.str().str(), + "protocol MyCollection {}"); + } +} diff --git a/utils/gyb_syntax_support/DeclNodes.py b/utils/gyb_syntax_support/DeclNodes.py index aa71a82cb14e0..7d261ec4ddda4 100644 --- a/utils/gyb_syntax_support/DeclNodes.py +++ b/utils/gyb_syntax_support/DeclNodes.py @@ -257,6 +257,9 @@ collection_element_name='Modifier', is_optional=True), Child('ProtocolKeyword', kind='ProtocolToken'), Child('Identifier', kind='IdentifierToken'), + Child('PrimaryAssociatedTypeClause', + kind='PrimaryAssociatedTypeClause', + is_optional=True), Child('InheritanceClause', kind='TypeInheritanceClause', is_optional=True), Child('GenericWhereClause', kind='GenericWhereClause', diff --git a/utils/gyb_syntax_support/GenericNodes.py b/utils/gyb_syntax_support/GenericNodes.py index 69ae1a4bb5c9a..67ed4faba4ea5 100644 --- a/utils/gyb_syntax_support/GenericNodes.py +++ b/utils/gyb_syntax_support/GenericNodes.py @@ -101,4 +101,13 @@ Child('Colon', kind='ColonToken'), Child('RightTypeIdentifier', kind='Type'), ]), + + # primary-associated-type-clause -> '<' primary-associated-type-list '>' + Node('PrimaryAssociatedTypeClause', kind='Syntax', + children=[ + Child('LeftAngleBracket', kind='LeftAngleToken'), + Child('PrimaryAssociatedTypeList', kind='PrimaryAssociatedTypeList', + collection_element_name='PrimaryAssociatedType'), + Child('RightAngleBracket', kind='RightAngleToken'), + ]), ] diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 675ad7d3b1a1a..19e721710c610 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -257,6 +257,7 @@ 'RegexLiteralExpr': 253, 'PrimaryAssociatedTypeList' : 254, 'PrimaryAssociatedType' : 255, + 'PrimaryAssociatedTypeClause' : 256, } From f518c80f4b6440a3dbd500b591eb065d6ae9ece1 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 23:21:56 -0400 Subject: [PATCH 159/242] Move validation-test/compiler_crashers_2/sr8968.swift to test/Generics/ since it passes with the Requirement Machine --- test/Generics/sr8968.swift | 56 +++++++++++++++++++ .../compiler_crashers_2/sr8968.swift | 48 ---------------- 2 files changed, 56 insertions(+), 48 deletions(-) create mode 100644 test/Generics/sr8968.swift delete mode 100644 validation-test/compiler_crashers_2/sr8968.swift diff --git a/test/Generics/sr8968.swift b/test/Generics/sr8968.swift new file mode 100644 index 0000000000000..b84cdd2c5cee4 --- /dev/null +++ b/test/Generics/sr8968.swift @@ -0,0 +1,56 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on +// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s + +public class OFMAttachment : NativeInserting { +// expected-warning@-1 {{non-final class 'OFMAttachment' cannot safely conform to protocol 'NativeInserting', which requires that 'Self.SnapshotType.NativeType' is exactly equal to 'Self'; this is an error in Swift 6}} + public typealias SnapshotType = Message.Attachment +} + +// CHECK-LABEL: .Snapshotting@ +// CHECK-NEXT: Requirement signature: +public protocol Snapshotting { + associatedtype NativeType: NativeInserting where NativeType.SnapshotType == Self + associatedtype RecordType: SnapshotRecord where RecordType.SnapshotType == Self + associatedtype ChangeType: SnapshotChange where ChangeType.SnapshotType == Self + + static var baseMessageName: String { get } +} + +// CHECK-LABEL: .NativeInserting@ +// CHECK-NEXT: Requirement signature: +public protocol NativeInserting { + associatedtype SnapshotType : Snapshotting where SnapshotType.NativeType == Self +} + +// CHECK-LABEL: .SnapshotRecord@ +// CHECK-NEXT: Requirement signature: +public protocol SnapshotRecord { + associatedtype SnapshotType : Snapshotting where SnapshotType.RecordType == Self + +} + +// CHECK-LABEL: .SnapshotChange@ +// CHECK-NEXT: Requirement signature: +public protocol SnapshotChange { + associatedtype SnapshotType : Snapshotting where SnapshotType.ChangeType == Self +} + +public struct Message { + public enum Attachment : Snapshotting { + + public static var baseMessageName: String = "attachment" + + public typealias NativeType = OFMAttachment + public typealias RecordType = Record + public typealias ChangeType = Change + public struct Record : SnapshotRecord { + public typealias SnapshotType = Message.Attachment + } + + public struct Change : SnapshotChange { + public typealias SnapshotType = Message.Attachment + } + + } +} + diff --git a/validation-test/compiler_crashers_2/sr8968.swift b/validation-test/compiler_crashers_2/sr8968.swift deleted file mode 100644 index b42be9911ef5e..0000000000000 --- a/validation-test/compiler_crashers_2/sr8968.swift +++ /dev/null @@ -1,48 +0,0 @@ -// RUN: not --crash %target-swift-frontend -emit-ir %s - -// REQUIRES: asserts - -public class OFMAttachment : NativeInserting { - public typealias SnapshotType = Message.Attachment -} - -public protocol Snapshotting { - associatedtype NativeType: NativeInserting where NativeType.SnapshotType == Self - associatedtype RecordType: SnapshotRecord where RecordType.SnapshotType == Self - associatedtype ChangeType: SnapshotChange where ChangeType.SnapshotType == Self - - static var baseMessageName: String { get } -} - -public protocol NativeInserting { - associatedtype SnapshotType : Snapshotting where SnapshotType.NativeType == Self -} - -public protocol SnapshotRecord { - associatedtype SnapshotType : Snapshotting where SnapshotType.RecordType == Self - -} - -public protocol SnapshotChange { - associatedtype SnapshotType : Snapshotting where SnapshotType.ChangeType == Self -} - -public struct Message { - public enum Attachment : Snapshotting { - - public static var baseMessageName: String = "attachment" - - public typealias NativeType = OFMAttachment - public typealias RecordType = Record - public typealias ChangeType = Change - public struct Record : SnapshotRecord { - public typealias SnapshotType = Message.Attachment - } - - public struct Change : SnapshotChange { - public typealias SnapshotType = Message.Attachment - } - - } -} - From 5eb29ae26ea42622ffc5f17fc232bd68409f59cb Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 16:43:12 -0400 Subject: [PATCH 160/242] GSB: Relax -requirement-machine-protocol-signatures=verify check a little We're getting too many false positives. If the Requirement Machine's result is a prefix of the GSB's result, and the extra requirements produced by the GSB are all same-type parameters, assume it's a GSB bug. --- lib/AST/GenericSignatureBuilder.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index f205b7745a5a9..1a8274e7354a1 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -8662,23 +8662,24 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator, auto compare = [&](ArrayRef rqmResult, ArrayRef gsbResult) { - if (proto->getParentModule()->isStdlibModule() && - (proto->getName().is("Collection") || - proto->getName().is("StringProtocol"))) { - if (rqmResult.size() > gsbResult.size()) - return false; - } else { - if (rqmResult.size() != gsbResult.size()) + if (rqmResult.size() > gsbResult.size()) + return false; + + if (!std::equal(rqmResult.begin(), + rqmResult.end(), + gsbResult.begin(), + [](const Requirement &lhs, + const Requirement &rhs) { + return lhs.getCanonical() == rhs.getCanonical(); + })) + return false; + + for (auto req : gsbResult.slice(rqmResult.size())) { + if (req.getKind() != RequirementKind::SameType) return false; } - return std::equal(rqmResult.begin(), - rqmResult.end(), - gsbResult.begin(), - [](const Requirement &lhs, - const Requirement &rhs) { - return lhs.getCanonical() == rhs.getCanonical(); - }); + return true; }; switch (ctx.LangOpts.RequirementMachineProtocolSignatures) { From 7c1c9cea94c3d6daac53e48785b263d1fa979a21 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Sat, 19 Feb 2022 08:08:30 -0800 Subject: [PATCH 161/242] [SILOpt] Disabled DestroyHoisting. --- lib/SILOptimizer/PassManager/PassPipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILOptimizer/PassManager/PassPipeline.cpp b/lib/SILOptimizer/PassManager/PassPipeline.cpp index 13eb1599d5601..0401dc5c780c3 100644 --- a/lib/SILOptimizer/PassManager/PassPipeline.cpp +++ b/lib/SILOptimizer/PassManager/PassPipeline.cpp @@ -67,7 +67,7 @@ llvm::cl::opt SILDisableLateOMEByDefault( "Disable late OME for non-transparent functions by default")); llvm::cl::opt - EnableDestroyHoisting("enable-destroy-hoisting", llvm::cl::init(true), + EnableDestroyHoisting("enable-destroy-hoisting", llvm::cl::init(false), llvm::cl::desc("Enable the DestroyHoisting pass.")); //===----------------------------------------------------------------------===// From 1a0cd33ca011996b34b9403d7b63aaa57132bb02 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 25 Feb 2022 11:06:46 -0800 Subject: [PATCH 162/242] [Benchmark] Shortened local var lifetime. --- benchmark/single-source/SubstringTest.swift | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/benchmark/single-source/SubstringTest.swift b/benchmark/single-source/SubstringTest.swift index bbf82480d1a52..83ddfcd652c98 100644 --- a/benchmark/single-source/SubstringTest.swift +++ b/benchmark/single-source/SubstringTest.swift @@ -63,11 +63,15 @@ public func run_SubstringFromLongStringGeneric(_ n: Int) { } } -@inline(never) -public func run_StringFromLongWholeSubstring(_ n: Int) { +private func getLongWideRealBuffer() -> String { var s0 = longWide s0 += "!" // ensure the string has a real buffer - let s = Substring(s0) + return s0 +} + +@inline(never) +public func run_StringFromLongWholeSubstring(_ n: Int) { + let s = Substring(getLongWideRealBuffer()) for _ in 1...n*500 { blackHole(String(s)) } @@ -75,9 +79,7 @@ public func run_StringFromLongWholeSubstring(_ n: Int) { @inline(never) public func run_StringFromLongWholeSubstringGeneric(_ n: Int) { - var s0 = longWide - s0 += "!" // ensure the string has a real buffer - let s = Substring(s0) + let s = Substring(getLongWideRealBuffer()) for _ in 1...n*500 { create(String.self, from: s) } From 74bf5cf45c4e6b306f551a3751f58807ec114650 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Tue, 22 Feb 2022 13:41:40 -0800 Subject: [PATCH 163/242] [Test] Tweaked test name. --- test/SILOptimizer/hoist_destroy_addr.sil | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/SILOptimizer/hoist_destroy_addr.sil b/test/SILOptimizer/hoist_destroy_addr.sil index 3865e6405db6c..957c5c41c9dac 100644 --- a/test/SILOptimizer/hoist_destroy_addr.sil +++ b/test/SILOptimizer/hoist_destroy_addr.sil @@ -350,12 +350,12 @@ exit: // Hoist a destroy_addr of an @in argument over a load from an alloc_stack. // -// CHECK-LABEL: sil [ossa] @test_hoist_over_load_from_stack : {{.*}} { +// CHECK-LABEL: sil [ossa] @hoist_over_load_from_stack : {{.*}} { // CHECK: apply // CHECK: destroy_addr // CHECK: load [take] -// CHECK-LABEL: } // end sil function 'test_hoist_over_load_from_stack' -sil [ossa] @test_hoist_over_load_from_stack : $@convention(thin) (@in X) -> @owned X { +// CHECK-LABEL: } // end sil function 'hoist_over_load_from_stack' +sil [ossa] @hoist_over_load_from_stack : $@convention(thin) (@in X) -> @owned X { entry(%in_addr : $*X): %stack_addr = alloc_stack $X copy_addr %in_addr to [initialization] %stack_addr : $*X From 672ec76d5494f9d51ab53fc6b6d5a83e4c6480cb Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Wed, 23 Feb 2022 17:05:09 -0800 Subject: [PATCH 164/242] [NFC] Removed spurious namespacing. --- lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp index b0b7630887580..9b131b852fb79 100644 --- a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp @@ -210,7 +210,7 @@ class DeinitBarriers { // sure not to hoist a destroy_addr into an access scope and by doing so cause // a deinit which had previously executed outside an access scope to start // executing within it--that could violate exclusivity. - llvm::SmallPtrSet barrierAccessScopes; + SmallPtrSet barrierAccessScopes; explicit DeinitBarriers(bool ignoreDeinitBarriers, const KnownStorageUses &knownUses, From a9e92111fcfb2ae61ab06bd61ec60eddb76d8cf8 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 14:03:48 -0700 Subject: [PATCH 165/242] [ConstraintSystem] Augment condition/ternary simplication to support statements --- include/swift/Sema/ConstraintSystem.h | 7 +++++++ lib/Sema/ConstraintSystem.cpp | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index e37baa80e0ad9..1fe0dd2f0648f 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -566,6 +566,13 @@ template T *getAsDecl(ASTNode node) { return nullptr; } +template +T *getAsStmt(ASTNode node) { + if (auto *S = node.dyn_cast()) + return dyn_cast_or_null(S); + return nullptr; +} + SourceLoc getLoc(ASTNode node); SourceRange getSourceRange(ASTNode node); diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 41969889553af..c388a2733829c 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4817,16 +4817,28 @@ void constraints::simplifyLocator(ASTNode &anchor, } case ConstraintLocator::Condition: { - anchor = castToExpr(anchor)->getCondExpr(); + if (auto *condStmt = getAsStmt(anchor)) { + anchor = &condStmt->getCond().front(); + } else { + anchor = castToExpr(anchor)->getCondExpr(); + } + path = path.slice(1); continue; } case ConstraintLocator::TernaryBranch: { auto branch = path[0].castTo(); - auto *ifExpr = castToExpr(anchor); - anchor = branch.forThen() ? ifExpr->getThenExpr() : ifExpr->getElseExpr(); + if (auto *ifStmt = getAsStmt(anchor)) { + anchor = + branch.forThen() ? ifStmt->getThenStmt() : ifStmt->getElseStmt(); + } else { + auto *ifExpr = castToExpr(anchor); + anchor = + branch.forThen() ? ifExpr->getThenExpr() : ifExpr->getElseExpr(); + } + path = path.slice(1); continue; } From eb6b26784880ef99da27c4adad7d297440065c01 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 14:47:21 -0700 Subject: [PATCH 166/242] [ConstraintSystem] Implement simplification of Witness, WrappedValue, OptionalPayload, and IUO Choice --- lib/Sema/ConstraintSystem.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index c388a2733829c..c98655487291b 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4935,6 +4935,12 @@ void constraints::simplifyLocator(ASTNode &anchor, case ConstraintLocator::ImplicitConversion: break; + + case ConstraintLocator::Witness: + case ConstraintLocator::WrappedValue: + case ConstraintLocator::OptionalPayload: + case ConstraintLocator::ImplicitlyUnwrappedDisjunctionChoice: + break; } // If we get here, we couldn't simplify the path further. From 01691d6497ee08d0636a63495ed1fde5ca8df2f0 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 15:08:13 -0700 Subject: [PATCH 167/242] [ConstraintSystem] ExpressionTimer: Simplify locators only if allotted time has been exceeded --- lib/Sema/ConstraintSystem.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index c98655487291b..be39c170c6772 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -76,6 +76,11 @@ ExpressionTimer::~ExpressionTimer() { if (!PrintWarning) return; + const auto WarnLimit = getWarnLimit(); + + if (WarnLimit == 0 || elapsedMS < WarnLimit) + return; + ASTNode anchor; if (auto *locator = Anchor.dyn_cast()) { anchor = simplifyLocatorToAnchor(locator); @@ -87,9 +92,7 @@ ExpressionTimer::~ExpressionTimer() { anchor = Anchor.get(); } - const auto WarnLimit = getWarnLimit(); - if (WarnLimit != 0 && elapsedMS >= WarnLimit && - anchor.getStartLoc().isValid()) { + if (anchor.getStartLoc().isValid()) { Context.Diags .diagnose(anchor.getStartLoc(), diag::debug_long_expression, elapsedMS, WarnLimit) From 2c35b651f4d2e3711d07315ba972e09a7a817480 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Wed, 16 Mar 2022 15:39:38 -0700 Subject: [PATCH 168/242] [CSDiagnostics] Adjust a couple of diagnostics to not respect different anchor kinds --- lib/Sema/CSDiagnostics.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index e8d1ae96e060f..441a4e9381163 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -3809,7 +3809,10 @@ bool MissingMemberFailure::diagnoseForDynamicCallable() const { } bool MissingMemberFailure::diagnoseInLiteralCollectionContext() const { - auto *expr = castToExpr(getAnchor()); + auto *expr = getAsExpr(getAnchor()); + if (!expr) + return false; + auto *parentExpr = findParentExpr(expr); auto &solution = getSolution(); @@ -5918,8 +5921,11 @@ void MissingGenericArgumentsFailure::emitGenericSignatureNote( return (type == params.end()) ? Type() : type->second; }; + auto baseType = anchor.dyn_cast(); + if (!baseType) + return; + SmallString<64> paramsAsString; - auto baseType = anchor.get(); if (TypeChecker::getDefaultGenericArgumentsString(paramsAsString, GTD, getPreferredType)) { auto diagnostic = emitDiagnosticAt( From 4af49e3479130c16810570dab61c5228dcdbfb91 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 17 Mar 2022 09:03:17 +0100 Subject: [PATCH 169/242] [CodeCompletion] Add test cases Add test cases for three issues that have been fixed on main - SR-14693 - SR-14704 - SR-14739 --- .../IDE/crashers_2_fixed/sr14693.swift | 25 ++++++++++++ .../IDE/crashers_2_fixed/sr14704.swift | 31 +++++++++++++++ .../IDE/crashers_2_fixed/sr14739.swift | 38 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 validation-test/IDE/crashers_2_fixed/sr14693.swift create mode 100644 validation-test/IDE/crashers_2_fixed/sr14704.swift create mode 100644 validation-test/IDE/crashers_2_fixed/sr14739.swift diff --git a/validation-test/IDE/crashers_2_fixed/sr14693.swift b/validation-test/IDE/crashers_2_fixed/sr14693.swift new file mode 100644 index 0000000000000..4072c2825480f --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/sr14693.swift @@ -0,0 +1,25 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=COMPLETE + +@resultBuilder public struct ViewBuilder2 { + public static func buildBlock(_ content: Content) -> Never +} + +public struct NavigationLink2 { + public init(destination: Destination, @ViewBuilder2 label: () -> Label) + public init(_ titleKey: String, destination: Destination) +} + +struct MoviePosterImage2 { + var imageLoader: Movie2 +} +struct MovieDetail2 {} +struct Movie2 {} + +struct MoviesHomeGridMoviesRow { + func foo() { + let movie: Movie2 + _ = NavigationLink2(destination: MovieDetail2()) { + MoviePosterImage2(imageLoader: movie.#^COMPLETE^# + +struct MoviesHomeGridMoviesRow_Previews { +} diff --git a/validation-test/IDE/crashers_2_fixed/sr14704.swift b/validation-test/IDE/crashers_2_fixed/sr14704.swift new file mode 100644 index 0000000000000..d41e76df4d996 --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/sr14704.swift @@ -0,0 +1,31 @@ +// RUN: %swift-ide-test --code-completion --source-filename %s --code-completion-token=COMPLETE + +@resultBuilder struct ViewBuilder2 { + static func buildBlock() -> Never { fatalError() } + static func buildBlock(_ content: Content) -> Content where Content : View2 { fatalError() } +} + +protocol View2 {} + +extension View2 { + func qadding(_ edges: Set2) -> some View2 { fatalError() } + func pnAppear(perform action: (() -> Void)? = nil) -> some View2 { fatalError() } +} + +struct EmptyView2: View2 {} + +struct Set2 { + static let bottom = Set2() +} + + +struct AdaptsToSoftwareKeyboard { + + @ViewBuilder2 func body(content: EmptyView2) -> some View2 { + content + .qadding(.#^COMPLETE^#bottom) + .pnAppear(perform: subscribeToKeyboardEvents) + } + + private func subscribeToKeyboardEvents() {} +} diff --git a/validation-test/IDE/crashers_2_fixed/sr14739.swift b/validation-test/IDE/crashers_2_fixed/sr14739.swift new file mode 100644 index 0000000000000..6b4b4507439ed --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/sr14739.swift @@ -0,0 +1,38 @@ +// RUN: %swift-ide-test --code-completion --source-filename %s --code-completion-token=COMPLETE + +struct ItemDetailView { + private var itemViewModel: Int + @ViewBuilder2 var body: some View2 { + Text2() + .environmens(\.horizontalSizeClass2, .#^COMPLETE^#regular) + .onDisappeaq { + self.itemViewModel + } + } +} + +protocol View2 {} + +extension View2 { + func onDisappeaq(perform action: (() -> Swift.Void)? = nil) -> some View2 { + fatalError() + } +} + +@resultBuilder struct ViewBuilder2 { + static func buildBlock() -> Never { fatalError() } + static func buildBlock(_ content: Content) -> Content where Content : View2 { fatalError() } +} + +enum Foo { + case regular +} + +struct EnvironmentValues2 { + public var horizontalSizeClass2: Foo +} + +public struct Text2 : View2 { + public init() { fatalError() } + func environmens(_ keyPath: WritableKeyPath, _ value: V) -> some View2 { fatalError() } +} From ca9056a275567ac5c5ccf33f9053d01802d550fc Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 3 Mar 2022 09:45:05 +0100 Subject: [PATCH 170/242] [TypeChecker] Add method to check whether a type variable represents a code completion token --- include/swift/Sema/ConstraintSystem.h | 4 ++++ lib/Sema/TypeCheckConstraints.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index e37baa80e0ad9..ad9f62b72c835 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -386,6 +386,10 @@ class TypeVariableType::Implementation { bool isTypeSequence() const; + /// Determine whether this type variable represents a code completion + /// expression. + bool isCodeCompletionToken() const; + /// Retrieve the representative of the equivalence class to which this /// type variable belongs. /// diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index fd25525dae56a..ca87285584fdf 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -130,6 +130,10 @@ bool TypeVariableType::Implementation::isTypeSequence() const { && locator->getGenericParameter()->isTypeSequence(); } +bool TypeVariableType::Implementation::isCodeCompletionToken() const { + return locator && locator->directlyAt(); +} + void *operator new(size_t bytes, ConstraintSystem& cs, size_t alignment) { return cs.getAllocator().Allocate(bytes, alignment); From ea2972346644c9d130906840ea1bfa63d548d3af Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 14 Mar 2022 10:43:00 +0900 Subject: [PATCH 171/242] [Distributed] Add name parameter to recordArgument for better interop --- lib/AST/DistributedDecl.cpp | 96 +++++++++++-------- lib/Sema/CodeSynthesisDistributedActor.cpp | 5 + lib/Sema/TypeCheckDistributed.cpp | 2 +- .../Distributed/DistributedActorSystem.swift | 3 +- .../Inputs/BadDistributedActorSystems.swift | 4 +- .../Inputs/FakeDistributedActorSystems.swift | 8 +- .../Inputs/dynamic_replacement_da_decl.swift | 2 +- .../Runtime/distributed_actor_decode.swift | 2 +- .../Runtime/distributed_actor_deinit.swift | 2 +- ...or_func_calls_remoteCall_genericFunc.swift | 8 +- .../distributed_actor_init_local.swift | 2 +- .../distributed_actor_remoteCall.swift | 36 +++---- ...stributed_actor_remoteCall_roundtrip.swift | 2 +- .../distributed_actor_remote_functions.swift | 2 +- ...tributed_actor_remote_retains_system.swift | 2 +- .../distributed_actor_self_calls.swift | 2 +- ...stem_missing_adhoc_requirement_impls.swift | 18 ++-- test/IRGen/distributed_actor.swift | 2 +- test/SILGen/distributed_thunk.swift | 2 +- .../Inputs/def_distributed.swift | 2 +- test/TBD/distributed.swift | 2 +- .../protocol/special/DistributedActor.swift | 2 +- 22 files changed, 113 insertions(+), 93 deletions(-) diff --git a/lib/AST/DistributedDecl.cpp b/lib/AST/DistributedDecl.cpp index d3f61b78e8091..ed9d2b69c71d3 100644 --- a/lib/AST/DistributedDecl.cpp +++ b/lib/AST/DistributedDecl.cpp @@ -635,60 +635,72 @@ AbstractFunctionDecl::isDistributedTargetInvocationEncoderRecordArgument() const // === Check all parameters auto params = getParameters(); - if (params->size() != 1) { + if (params->size() != 2) { return false; } - // --- Check parameter: _ argument - auto argumentParam = params->get(0); - if (!argumentParam->getArgumentName().is("")) { - return false; - } + // --- Check parameter: label + auto labelParam = params->get(0); + if (!labelParam->getArgumentName().is("name")) { + return false; + } + if (!labelParam->getInterfaceType()->isEqual(C.getStringType())) { + return false; + } - // === Check generic parameters in detail - // --- Check: Argument: SerializationRequirement - GenericTypeParamDecl *ArgumentParam = genericParams->getParams()[0]; + // --- Check parameter: _ argument + auto argumentParam = params->get(1); + if (!argumentParam->getArgumentName().is("")) { + return false; + } - auto sig = getGenericSignature(); - auto requirements = sig.getRequirements(); + // === Check generic parameters in detail + // --- Check: Argument: SerializationRequirement + GenericTypeParamDecl *ArgumentParam = genericParams->getParams()[0]; - if (requirements.size() != expectedRequirementsNum) { - return false; - } + auto sig = getGenericSignature(); + auto requirements = sig.getRequirements(); - // --- Check the expected requirements - // --- all the Argument requirements --- - // conforms_to: Argument Decodable - // conforms_to: Argument Encodable - // ... + if (requirements.size() != expectedRequirementsNum) { + return false; + } - auto func = dyn_cast(this); - if (!func) { - return false; - } + // --- Check the expected requirements + // --- all the Argument requirements --- + // e.g. + // conforms_to: Argument Decodable + // conforms_to: Argument Encodable + // ... - auto resultType = func->mapTypeIntoContext(argumentParam->getInterfaceType()) - ->getDesugaredType(); - auto resultParamType = func->mapTypeIntoContext( - ArgumentParam->getInterfaceType()->getMetatypeInstanceType()); - // The result of the function must be the `Res` generic argument. - if (!resultType->isEqual(resultParamType)) { - return false; - } + auto func = dyn_cast(this); + if (!func) { + return false; + } - for (auto requirementProto : requirementProtos) { - auto conformance = module->lookupConformance(resultType, requirementProto); - if (conformance.isInvalid()) { + auto resultType = + func->mapTypeIntoContext(argumentParam->getInterfaceType()) + ->getDesugaredType(); + auto resultParamType = func->mapTypeIntoContext( + ArgumentParam->getInterfaceType()->getMetatypeInstanceType()); + // The result of the function must be the `Res` generic argument. + if (!resultType->isEqual(resultParamType)) { return false; } - } - // === Check result type: Void - if (!func->getResultInterfaceType()->isVoid()) { - return false; - } + for (auto requirementProto : requirementProtos) { + auto conformance = + module->lookupConformance(resultType, requirementProto); + if (conformance.isInvalid()) { + return false; + } + } - return true; + // === Check result type: Void + if (!func->getResultInterfaceType()->isVoid()) { + return false; + } + + return true; } bool @@ -879,8 +891,8 @@ AbstractFunctionDecl::isDistributedTargetInvocationEncoderRecordErrorType() cons } // --- Check parameter: _ errorType - auto argumentParam = params->get(0); - if (!argumentParam->getArgumentName().is("")) { + auto errorTypeParam = params->get(0); + if (!errorTypeParam->getArgumentName().is("")) { return false; } diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 3516f0108a6e4..5f18152bf20e5 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -269,9 +269,14 @@ deriveBodyDistributed_thunk(AbstractFunctionDecl *thunk, void *context) { auto recordArgumentDeclRef = UnresolvedDeclRefExpr::createImplicit( C, recordArgumentDecl->getName()); + auto argumentName = param->getArgumentName().str(); auto recordArgArgsList = ArgumentList::forImplicitCallTo( recordArgumentDeclRef->getName(), { + // name: + new (C) StringLiteralExpr(argumentName, SourceRange(), + /*implicit=*/true), + // _ argument: new (C) DeclRefExpr( ConcreteDeclRef(param), dloc, implicit, AccessSemantics::Ordinary, diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp index 9fa49fbde9f88..5f681b3ddc01d 100644 --- a/lib/Sema/TypeCheckDistributed.cpp +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -305,7 +305,7 @@ bool swift::checkDistributedActorSystemAdHocProtocolRequirements( decl->getDescriptiveKind(), decl->getName(), identifier); decl->diagnose(diag::note_distributed_actor_system_conformance_missing_adhoc_requirement, decl->getName(), identifier, - "mutating func recordArgument(_ argument: Argument) throws\n"); + "mutating func recordArgument(name: String, _ argument: Argument) throws\n"); anyMissingAdHocRequirements = true; } if (checkAdHocRequirementAccessControl(decl, Proto, recordArgumentDecl)) diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index 9519f281b4777..64b9443192d38 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -418,8 +418,7 @@ public protocol DistributedTargetInvocationEncoder { // /// // /// Record an argument of `Argument` type. // /// This will be invoked for every argument of the target, in declaration order. -// mutating func recordArgument(_ argument: Argument) throws - // TODO(distributed): offer recordArgument(label:type:) +// mutating func recordArgument(name: String, _ argument: Argument) throws /// Record the error type of the distributed method. /// This method will not be invoked if the target is not throwing. diff --git a/test/Distributed/Inputs/BadDistributedActorSystems.swift b/test/Distributed/Inputs/BadDistributedActorSystems.swift index 8303860f568f9..4716262009115 100644 --- a/test/Distributed/Inputs/BadDistributedActorSystems.swift +++ b/test/Distributed/Inputs/BadDistributedActorSystems.swift @@ -257,8 +257,8 @@ public struct FakeInvocationEncoder : DistributedTargetInvocationEncoder { genericSubs.append(type) } - public mutating func recordArgument(_ argument: Argument) throws { - print(" > encode argument: \(argument)") + public mutating func recordArgument(name: String, _ argument: Argument) throws { + print(" > encode argument name:\(name), argument: \(argument)") arguments.append(argument) } public mutating func recordErrorType(_ type: E.Type) throws { diff --git a/test/Distributed/Inputs/FakeDistributedActorSystems.swift b/test/Distributed/Inputs/FakeDistributedActorSystems.swift index d9f8611f6e5ad..87c7e839d4876 100644 --- a/test/Distributed/Inputs/FakeDistributedActorSystems.swift +++ b/test/Distributed/Inputs/FakeDistributedActorSystems.swift @@ -267,18 +267,22 @@ public struct FakeInvocationEncoder : DistributedTargetInvocationEncoder { genericSubs.append(type) } - public mutating func recordArgument(_ argument: Argument) throws { - print(" > encode argument: \(argument)") + public mutating func recordArgument( + name: String, _ argument: Argument) throws { + print(" > encode argument name:\(name), value: \(argument)") arguments.append(argument) } + public mutating func recordErrorType(_ type: E.Type) throws { print(" > encode error type: \(String(reflecting: type))") self.errorType = type } + public mutating func recordReturnType(_ type: R.Type) throws { print(" > encode return type: \(String(reflecting: type))") self.returnType = type } + public mutating func doneRecording() throws { print(" > done recording") } diff --git a/test/Distributed/Inputs/dynamic_replacement_da_decl.swift b/test/Distributed/Inputs/dynamic_replacement_da_decl.swift index 564db85955074..1e3aa0468e87f 100644 --- a/test/Distributed/Inputs/dynamic_replacement_da_decl.swift +++ b/test/Distributed/Inputs/dynamic_replacement_da_decl.swift @@ -87,7 +87,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_decode.swift b/test/Distributed/Runtime/distributed_actor_decode.swift index 8f602a35c0d53..4902e144b88d5 100644 --- a/test/Distributed/Runtime/distributed_actor_decode.swift +++ b/test/Distributed/Runtime/distributed_actor_decode.swift @@ -105,7 +105,7 @@ class FakeInvocation: DistributedTargetInvocationEncoder, DistributedTargetInvoc typealias SerializationRequirement = Codable func recordGenericSubstitution(_ type: T.Type) throws {} - func recordArgument(_ argument: Argument) throws {} + func recordArgument(name: String, _ argument: Argument) throws {} func recordReturnType(_ type: R.Type) throws {} func recordErrorType(_ type: E.Type) throws {} func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_deinit.swift b/test/Distributed/Runtime/distributed_actor_deinit.swift index f32524efd361a..faae2e024f449 100644 --- a/test/Distributed/Runtime/distributed_actor_deinit.swift +++ b/test/Distributed/Runtime/distributed_actor_deinit.swift @@ -123,7 +123,7 @@ class FakeDistributedInvocation: DistributedTargetInvocationEncoder, Distributed typealias SerializationRequirement = Codable func recordGenericSubstitution(_ type: T.Type) throws { } - func recordArgument(_ argument: Argument) throws { } + func recordArgument(name: String, _ argument: Argument) throws { } func recordReturnType(_ type: R.Type) throws { } func recordErrorType(_ type: E.Type) throws { } func doneRecording() throws { } diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift index 5c9922d8ac32d..2f0649b59c739 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift @@ -40,7 +40,7 @@ func test() async throws { let r1 = try await ref.generic("Caplin") // CHECK: > encode generic sub: Swift.String - // CHECK: > encode argument: Caplin + // CHECK: > encode argument name:, value: Caplin // CHECK: > encode return type: Swift.String // CHECK: > done recording // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.generic(_:), invocation:FakeInvocationEncoder(genericSubs: [Swift.String], arguments: ["Caplin"], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String @@ -54,9 +54,9 @@ func test() async throws { ) // CHECK: > encode generic sub: Swift.String // CHECK: > encode generic sub: Swift.Int - // CHECK: > encode argument: 2.0 - // CHECK: > encode argument: Caplin - // CHECK: > encode argument: [1, 2, 3] + // CHECK: > encode argument name:strict, value: 2.0 + // CHECK: > encode argument name:, value: Caplin + // CHECK: > encode argument name:, value: [1, 2, 3] // CHECK: > encode return type: Swift.String // CHECK: > done recording // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.generic2(strict:_:_:), invocation:FakeInvocationEncoder(genericSubs: [Swift.String, Swift.Int], arguments: [2.0, "Caplin", [1, 2, 3]], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String diff --git a/test/Distributed/Runtime/distributed_actor_init_local.swift b/test/Distributed/Runtime/distributed_actor_init_local.swift index 2dcd74a111b53..f8cb6432144e3 100644 --- a/test/Distributed/Runtime/distributed_actor_init_local.swift +++ b/test/Distributed/Runtime/distributed_actor_init_local.swift @@ -126,7 +126,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_remoteCall.swift b/test/Distributed/Runtime/distributed_actor_remoteCall.swift index 498560c49ecc7..a36906a71fca0 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCall.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCall.swift @@ -182,7 +182,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { mutating func recordGenericSubstitution(_ type: T.Type) throws { substitutions.append(type) } - mutating func recordArgument(_ argument: Argument) throws { + mutating func recordArgument(name: String, _ argument: Argument) throws { arguments.append(argument) } mutating func recordErrorType(_ type: E.Type) throws { @@ -343,8 +343,8 @@ func test() async throws { // CHECK: RETURN: bar var echoInvocation = system.makeInvocationEncoder() - try echoInvocation.recordArgument("Caplin") - try echoInvocation.recordArgument(42) + try echoInvocation.recordArgument(name: "argument-name", "Caplin") + try echoInvocation.recordArgument(name: "argument-name", 42) try echoInvocation.doneRecording() var echoDecoder = echoInvocation.makeDecoder() @@ -359,7 +359,7 @@ func test() async throws { var generic1Invocation = system.makeInvocationEncoder() try generic1Invocation.recordGenericSubstitution(Int.self) - try generic1Invocation.recordArgument(42) + try generic1Invocation.recordArgument(name: "argument-name", 42) try generic1Invocation.doneRecording() var generic1Decoder = generic1Invocation.makeDecoder() @@ -376,8 +376,8 @@ func test() async throws { try generic2Invocation.recordGenericSubstitution(Int.self) try generic2Invocation.recordGenericSubstitution(String.self) - try generic2Invocation.recordArgument(42) - try generic2Invocation.recordArgument("Ultimate Question!") + try generic2Invocation.recordArgument(name: "argument-name", 42) + try generic2Invocation.recordArgument(name: "argument-name", "Ultimate Question!") try generic2Invocation.doneRecording() var generic2Decoder = generic2Invocation.makeDecoder() @@ -396,9 +396,9 @@ func test() async throws { try generic3Invocation.recordGenericSubstitution(Int.self) try generic3Invocation.recordGenericSubstitution(String.self) try generic3Invocation.recordGenericSubstitution(S.self) - try generic3Invocation.recordArgument(42) - try generic3Invocation.recordArgument(["a", "b", "c"]) - try generic3Invocation.recordArgument(S(data: 42)) + try generic3Invocation.recordArgument(name: "argument-name", 42) + try generic3Invocation.recordArgument(name: "argument-name", ["a", "b", "c"]) + try generic3Invocation.recordArgument(name: "argument-name", S(data: 42)) try generic3Invocation.doneRecording() var generic3Decoder = generic3Invocation.makeDecoder() @@ -418,9 +418,9 @@ func test() async throws { try generic4Invocation.recordGenericSubstitution(Int.self) try generic4Invocation.recordGenericSubstitution(Int.self) try generic4Invocation.recordGenericSubstitution(String.self) - try generic4Invocation.recordArgument(42) - try generic4Invocation.recordArgument(S(data: 42)) - try generic4Invocation.recordArgument(["a", "b", "c"]) + try generic4Invocation.recordArgument(name: "argument-name", 42) + try generic4Invocation.recordArgument(name: "argument-name", S(data: 42)) + try generic4Invocation.recordArgument(name: "argument-name", ["a", "b", "c"]) try generic4Invocation.doneRecording() var generic4Decoder = generic4Invocation.makeDecoder() @@ -441,10 +441,10 @@ func test() async throws { try generic5Invocation.recordGenericSubstitution(Int.self) try generic5Invocation.recordGenericSubstitution(String.self) try generic5Invocation.recordGenericSubstitution([Int].self) - try generic5Invocation.recordArgument(42) - try generic5Invocation.recordArgument(S(data: 42)) - try generic5Invocation.recordArgument("Hello, World!") - try generic5Invocation.recordArgument([0, 42]) + try generic5Invocation.recordArgument(name: "argument-name", 42) + try generic5Invocation.recordArgument(name: "argument-name", S(data: 42)) + try generic5Invocation.recordArgument(name: "argument-name", "Hello, World!") + try generic5Invocation.recordArgument(name: "argument-name", [0, 42]) try generic5Invocation.doneRecording() var generic5Decoder = generic5Invocation.makeDecoder() @@ -467,7 +467,7 @@ func test() async throws { var genericOptInvocation = system.makeInvocationEncoder() try genericOptInvocation.recordGenericSubstitution([Int].self) - try genericOptInvocation.recordArgument([0, 42]) + try genericOptInvocation.recordArgument(name: "argument-name", [0, 42]) try genericOptInvocation.doneRecording() var genericOptDecoder = genericOptInvocation.makeDecoder() @@ -482,7 +482,7 @@ func test() async throws { var decodeErrInvocation = system.makeInvocationEncoder() - try decodeErrInvocation.recordArgument(42) + try decodeErrInvocation.recordArgument(name: "argument-name", 42) try decodeErrInvocation.doneRecording() var decodeErrDecoder = decodeErrInvocation.makeDecoder() diff --git a/test/Distributed/Runtime/distributed_actor_remoteCall_roundtrip.swift b/test/Distributed/Runtime/distributed_actor_remoteCall_roundtrip.swift index fffb546d6e058..b68824964b88e 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCall_roundtrip.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCall_roundtrip.swift @@ -46,7 +46,7 @@ func test() async throws { let ref = try Greeter.resolve(id: local.id, using: system) let reply = try await ref.echo(name: "Caplin") - // CHECK: > encode argument: Caplin + // CHECK: > encode argument name:name, value: Caplin // CHECK-NOT: > encode error type // CHECK: > encode return type: Swift.String // CHECK: > done recording diff --git a/test/Distributed/Runtime/distributed_actor_remote_functions.swift b/test/Distributed/Runtime/distributed_actor_remote_functions.swift index a12103c470bb6..a613f061171a4 100644 --- a/test/Distributed/Runtime/distributed_actor_remote_functions.swift +++ b/test/Distributed/Runtime/distributed_actor_remote_functions.swift @@ -153,7 +153,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift b/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift index 831a9f0b47545..6bbc8dd24573c 100644 --- a/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift +++ b/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift @@ -101,7 +101,7 @@ class FakeInvocation: DistributedTargetInvocationEncoder, DistributedTargetInvoc typealias SerializationRequirement = Codable func recordGenericSubstitution(_ type: T.Type) throws {} - func recordArgument(_ argument: Argument) throws {} + func recordArgument(name: String, _ argument: Argument) throws {} func recordReturnType(_ type: R.Type) throws {} func recordErrorType(_ type: E.Type) throws {} func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_self_calls.swift b/test/Distributed/Runtime/distributed_actor_self_calls.swift index 2afcc63c9a1c1..0eccf8c7d139b 100644 --- a/test/Distributed/Runtime/distributed_actor_self_calls.swift +++ b/test/Distributed/Runtime/distributed_actor_self_calls.swift @@ -99,7 +99,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} diff --git a/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift b/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift index f0f89a1f3bc40..d69e3d8efa7b2 100644 --- a/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift +++ b/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift @@ -500,7 +500,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -510,7 +510,7 @@ struct AnyInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Any mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -520,7 +520,7 @@ struct LargeSerializationReqFakeInvocationEncoder: DistributedTargetInvocationEn typealias SerializationRequirement = Codable & SomeProtocol mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -530,7 +530,7 @@ public struct PublicFakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(_ argument: Argument) throws {} + public mutating func recordArgument(name: String, _ argument: Argument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} @@ -542,7 +542,7 @@ struct FakeInvocationEncoder_missing_recordArgument: DistributedTargetInvocation typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - // MISSING: mutating func recordArgument(_ argument: Argument) throws {} + // MISSING: mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -566,7 +566,7 @@ struct FakeInvocationEncoder_missing_recordReturnType: DistributedTargetInvocati typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} // MISSING: mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -577,7 +577,7 @@ struct FakeInvocationEncoder_missing_recordErrorType: DistributedTargetInvocatio typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} // MISSING: mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -603,7 +603,7 @@ struct FakeInvocationEncoder_recordResultType_wrongType: DistributedTargetInvoca typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(s: String, _ resultType: R.Type) throws {} // BAD mutating func recordReturnType(_ resultType: R.Type) throws {} // BAD mutating func recordReturnType(badName: R.Type) throws {} // BAD @@ -616,7 +616,7 @@ struct FakeInvocationEncoder_recordErrorType_wrongType: DistributedTargetInvocat typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(BadName type: E.Type) throws {} // BAD mutating func recordErrorType(_ type: E.Type) throws {} // BAD diff --git a/test/IRGen/distributed_actor.swift b/test/IRGen/distributed_actor.swift index 650b9c27b6fe3..40a7c379eb861 100644 --- a/test/IRGen/distributed_actor.swift +++ b/test/IRGen/distributed_actor.swift @@ -102,7 +102,7 @@ public struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(_ argument: Argument) throws {} + public mutating func recordArgument(name: String, _ argument: Argument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} diff --git a/test/SILGen/distributed_thunk.swift b/test/SILGen/distributed_thunk.swift index 8a7cef11da516..d574ef53392b7 100644 --- a/test/SILGen/distributed_thunk.swift +++ b/test/SILGen/distributed_thunk.swift @@ -106,7 +106,7 @@ public struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(_ argument: Argument) throws {} + public mutating func recordArgument(name: String, _ argument: Argument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} diff --git a/test/Serialization/Inputs/def_distributed.swift b/test/Serialization/Inputs/def_distributed.swift index 977d6962718e7..8b75215acaa08 100644 --- a/test/Serialization/Inputs/def_distributed.swift +++ b/test/Serialization/Inputs/def_distributed.swift @@ -99,7 +99,7 @@ public struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(_ argument: Argument) throws {} + public mutating func recordArgument(name: String, _ argument: Argument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} diff --git a/test/TBD/distributed.swift b/test/TBD/distributed.swift index 3de6fce702e1d..4807412561d17 100644 --- a/test/TBD/distributed.swift +++ b/test/TBD/distributed.swift @@ -115,7 +115,7 @@ public struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(_ argument: Argument) throws {} + public mutating func recordArgument(name: String, _ argument: Argument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} diff --git a/test/decl/protocol/special/DistributedActor.swift b/test/decl/protocol/special/DistributedActor.swift index 239059bcbacec..adb8202fd06b9 100644 --- a/test/decl/protocol/special/DistributedActor.swift +++ b/test/decl/protocol/special/DistributedActor.swift @@ -161,7 +161,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(_ argument: Argument) throws {} + mutating func recordArgument(name: String, _ argument: Argument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} From 3ff972b6f81631f3b6776af530edf1ec25f38834 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 15 Mar 2022 20:43:30 +0900 Subject: [PATCH 172/242] [Distributed] unlock some tests previously failing --- ...tributed_actor_func_calls_remoteCall_take_two.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift index ae1310ed60d10..fdb1a9a345e88 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_take_two.swift @@ -29,6 +29,12 @@ distributed actor Greeter { distributed func take(name: String, int: Int, clazz: SomeClass) { print("take: \(name), int: \(int), clazz: \(clazz)") } + + distributed func params(param p1: String, param p2: Int) -> String { + let message = "params: p1: \(p1), p2: \(p2)" + print(message) + return message + } } func test() async throws { @@ -40,8 +46,10 @@ func test() async throws { try await ref.take(name: "Caplin", int: 1337) // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.take(name:int:), invocation:FakeInvocationEncoder(genericSubs: [], arguments: ["Caplin", 1337], returnType: nil, errorType: nil), throwing:Swift.Never - // try await ref.take(name: "Caplin", int: 1337, clazz: .init()) // FIXME(distributed): crashes + try await ref.take(name: "Caplin", int: 1337, clazz: .init()) + let r3 = try await ref.params(param: "one", param: 2) + print("r3 = \(r3)") // CHECK: r3 = params: p1: one, p2: 2 } @main struct Main { From 20f28f8c3c65dfbb24c2f6556560accfd239dfb7 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 16 Mar 2022 13:54:17 +0900 Subject: [PATCH 173/242] [Distributed] make RemoteCallTarget Hashable, add availability --- stdlib/public/Distributed/DistributedActorSystem.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index 64b9443192d38..2b2ba6cddce9d 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -12,6 +12,7 @@ import Swift import _Concurrency +/// A distributed actor system @available(SwiftStdlib 5.7, *) public protocol DistributedActorSystem: Sendable { /// The identity used by actors that communicate via this transport @@ -340,7 +341,7 @@ extension DistributedActorSystem { /// The string representation will attempt to pretty print the target identifier, /// however its exact format is not specified and may change in future versions. @available(SwiftStdlib 5.7, *) -public struct RemoteCallTarget: CustomStringConvertible { +public struct RemoteCallTarget: CustomStringConvertible, Hashable { private let _identifier: String public init(_ identifier: String) { @@ -407,6 +408,7 @@ func _executeDistributedTarget( /// Note that the decoding will be provided the specific types that the sending side used to preform the call, /// so decoding can rely on simply invoking e.g. `Codable` (if that is the `SerializationRequirement`) decoding /// entry points on the provided types. +@available(SwiftStdlib 5.7, *) public protocol DistributedTargetInvocationEncoder { associatedtype SerializationRequirement @@ -418,7 +420,7 @@ public protocol DistributedTargetInvocationEncoder { // /// // /// Record an argument of `Argument` type. // /// This will be invoked for every argument of the target, in declaration order. -// mutating func recordArgument(name: String, _ argument: Argument) throws +// mutating func recordArgument(_ argument: Argument) throws /// Record the error type of the distributed method. /// This method will not be invoked if the target is not throwing. @@ -433,8 +435,12 @@ public protocol DistributedTargetInvocationEncoder { mutating func doneRecording() throws } +@available(SwiftStdlib 5.7, *) +public + /// Decoder that must be provided to `executeDistributedTarget` and is used /// by the Swift runtime to decode arguments of the invocation. +@available(SwiftStdlib 5.7, *) public protocol DistributedTargetInvocationDecoder { associatedtype SerializationRequirement From 5a5d1ba2e622f7514191ce18426b7aa834462c93 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 16 Mar 2022 19:21:24 +0900 Subject: [PATCH 174/242] [Distributed] Implement RemoteCallArgument --- include/swift/AST/Decl.h | 5 +- include/swift/AST/KnownSDKTypes.def | 1 + include/swift/AST/TypeCheckRequests.h | 21 ++++- include/swift/AST/TypeCheckerTypeIDZone.def | 3 + lib/AST/DistributedDecl.cpp | 83 +++++++++++-------- lib/Sema/CodeSynthesisDistributedActor.cpp | 63 ++++++++++++-- lib/Sema/TypeCheckDistributed.cpp | 44 +++++++++- .../Distributed/DistributedActorSystem.swift | 45 +++++++++- .../LocalTestingDistributedActorSystem.swift | 2 +- .../Inputs/BadDistributedActorSystems.swift | 4 +- .../Inputs/FakeDistributedActorSystems.swift | 8 +- .../Inputs/dynamic_replacement_da_decl.swift | 2 +- .../Runtime/distributed_actor_decode.swift | 4 +- .../Runtime/distributed_actor_deinit.swift | 8 +- ...or_func_calls_remoteCall_genericFunc.swift | 6 +- .../distributed_actor_init_local.swift | 2 +- .../distributed_actor_remoteCall.swift | 38 ++++----- .../distributed_actor_remote_functions.swift | 2 +- ...tributed_actor_remote_retains_system.swift | 2 +- .../distributed_actor_self_calls.swift | 2 +- ...stem_missing_adhoc_requirement_impls.swift | 29 +++++-- test/IRGen/distributed_actor.swift | 2 +- test/SILGen/distributed_thunk.swift | 2 +- .../Inputs/def_distributed.swift | 2 +- test/TBD/distributed.swift | 2 +- .../protocol/special/DistributedActor.swift | 2 +- 26 files changed, 282 insertions(+), 102 deletions(-) diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index 6910f2b27c710..07d3c2b31f88c 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3493,9 +3493,12 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { /// Find, or potentially synthesize, the implicit 'id' property of this actor. VarDecl *getDistributedActorIDProperty() const; - /// Find the 'RemoteCallTarget.init(_mangledName:)' initializer function + /// Find the 'RemoteCallTarget.init(_:)' initializer function ConstructorDecl* getDistributedRemoteCallTargetInitFunction() const; + /// Find the 'RemoteCallArgument(label:name:value:)' initializer function + ConstructorDecl* getDistributedRemoteCallArgumentInitFunction() const; + /// Collect the set of protocols to which this type should implicitly /// conform, such as AnyObject (for classes). void getImplicitProtocols(SmallVectorImpl &protocols); diff --git a/include/swift/AST/KnownSDKTypes.def b/include/swift/AST/KnownSDKTypes.def index 249fd18b07d82..747f1ad3d6931 100644 --- a/include/swift/AST/KnownSDKTypes.def +++ b/include/swift/AST/KnownSDKTypes.def @@ -49,6 +49,7 @@ KNOWN_SDK_TYPE_DECL(Distributed, DistributedActorSystem, ProtocolDecl, 0) KNOWN_SDK_TYPE_DECL(Distributed, DistributedTargetInvocationEncoder, ProtocolDecl, 0) KNOWN_SDK_TYPE_DECL(Distributed, DistributedTargetInvocationDecoder, ProtocolDecl, 0) KNOWN_SDK_TYPE_DECL(Distributed, RemoteCallTarget, StructDecl, 0) +KNOWN_SDK_TYPE_DECL(Distributed, RemoteCallArgument, StructDecl, 1) // String processing KNOWN_SDK_TYPE_DECL(StringProcessing, Regex, StructDecl, 1) diff --git a/include/swift/AST/TypeCheckRequests.h b/include/swift/AST/TypeCheckRequests.h index 239ca06f00c9a..cfb10f72cdd17 100644 --- a/include/swift/AST/TypeCheckRequests.h +++ b/include/swift/AST/TypeCheckRequests.h @@ -1210,7 +1210,7 @@ class GetDistributedActorSystemPropertyRequest : bool isCached() const { return true; } }; -/// Obtain the constructor of the RemoteCallTarget type. +/// Obtain the constructor of the 'RemoteCallTarget' type. class GetDistributedRemoteCallTargetInitFunctionRequest : public SimpleRequest { +public: + using SimpleRequest::SimpleRequest; + +private: + friend SimpleRequest; + + ConstructorDecl *evaluate(Evaluator &evaluator, + NominalTypeDecl *nominal) const; + +public: + // Caching + bool isCached() const { return true; } +}; + /// Obtain the 'distributed thunk' for the passed-in function. /// /// The thunk is responsible for invoking 'remoteCall' when invoked on a remote diff --git a/include/swift/AST/TypeCheckerTypeIDZone.def b/include/swift/AST/TypeCheckerTypeIDZone.def index 3fd6390dad523..2aa9b7172f3aa 100644 --- a/include/swift/AST/TypeCheckerTypeIDZone.def +++ b/include/swift/AST/TypeCheckerTypeIDZone.def @@ -145,6 +145,9 @@ SWIFT_REQUEST(TypeChecker, GetDistributedActorSystemPropertyRequest, SWIFT_REQUEST(TypeChecker, GetDistributedRemoteCallTargetInitFunctionRequest, ConstructorDecl *(NominalTypeDecl *), Cached, NoLocationInfo) +SWIFT_REQUEST(TypeChecker, GetDistributedRemoteCallArgumentInitFunctionRequest, + ConstructorDecl *(NominalTypeDecl *), + Cached, NoLocationInfo) SWIFT_REQUEST(TypeChecker, GetDistributedActorInvocationDecoderRequest, NominalTypeDecl *(NominalTypeDecl *), Cached, NoLocationInfo) diff --git a/lib/AST/DistributedDecl.cpp b/lib/AST/DistributedDecl.cpp index ed9d2b69c71d3..d5b6332a63785 100644 --- a/lib/AST/DistributedDecl.cpp +++ b/lib/AST/DistributedDecl.cpp @@ -581,6 +581,11 @@ AbstractFunctionDecl::isDistributedTargetInvocationEncoderRecordArgument() const auto &C = getASTContext(); auto module = getParentModule(); + auto func = dyn_cast(this); + if (!func) { + return false; + } + // === Check base name if (getBaseIdentifier() != C.Id_recordArgument) { return false; @@ -614,6 +619,12 @@ AbstractFunctionDecl::isDistributedTargetInvocationEncoderRecordArgument() const return false; } + // --- must be mutating, if it is defined in a struct + if (isa(getDeclContext()) && + !func->isMutating()) { + return false; + } + // --- Check number of generic parameters auto genericParams = getGenericParams(); unsigned int expectedGenericParamNum = 1; @@ -635,28 +646,43 @@ AbstractFunctionDecl::isDistributedTargetInvocationEncoderRecordArgument() const // === Check all parameters auto params = getParameters(); - if (params->size() != 2) { + if (params->size() != 1) { return false; } - // --- Check parameter: label - auto labelParam = params->get(0); - if (!labelParam->getArgumentName().is("name")) { - return false; - } - if (!labelParam->getInterfaceType()->isEqual(C.getStringType())) { + GenericTypeParamDecl *ArgumentParam = genericParams->getParams()[0]; + + // --- Check parameter: _ argument + auto argumentParam = params->get(0); + if (!argumentParam->getArgumentName().empty()) { return false; } - // --- Check parameter: _ argument - auto argumentParam = params->get(1); - if (!argumentParam->getArgumentName().is("")) { + auto argumentTy = argumentParam->getInterfaceType(); + auto argumentInContextTy = mapTypeIntoContext(argumentTy); + if (argumentInContextTy->getAnyNominal() == C.getRemoteCallArgumentDecl()) { + auto argGenericParams = argumentInContextTy->getStructOrBoundGenericStruct() + ->getGenericParams()->getParams(); + if (argGenericParams.size() != 1) { + return false; + } + + // the of the RemoteCallArgument + auto remoteCallArgValueGenericTy = + mapTypeIntoContext(argGenericParams[0]->getInterfaceType()) + ->getDesugaredType() + ->getMetatypeInstanceType(); + // expected (the from the recordArgument) + auto expectedGenericParamTy = mapTypeIntoContext( + ArgumentParam->getInterfaceType()->getMetatypeInstanceType()); + + if (!remoteCallArgValueGenericTy->isEqual(expectedGenericParamTy)) { + return false; + } + } else { return false; } - // === Check generic parameters in detail - // --- Check: Argument: SerializationRequirement - GenericTypeParamDecl *ArgumentParam = genericParams->getParams()[0]; auto sig = getGenericSignature(); auto requirements = sig.getRequirements(); @@ -672,29 +698,6 @@ AbstractFunctionDecl::isDistributedTargetInvocationEncoderRecordArgument() const // conforms_to: Argument Encodable // ... - auto func = dyn_cast(this); - if (!func) { - return false; - } - - auto resultType = - func->mapTypeIntoContext(argumentParam->getInterfaceType()) - ->getDesugaredType(); - auto resultParamType = func->mapTypeIntoContext( - ArgumentParam->getInterfaceType()->getMetatypeInstanceType()); - // The result of the function must be the `Res` generic argument. - if (!resultType->isEqual(resultParamType)) { - return false; - } - - for (auto requirementProto : requirementProtos) { - auto conformance = - module->lookupConformance(resultType, requirementProto); - if (conformance.isInvalid()) { - return false; - } - } - // === Check result type: Void if (!func->getResultInterfaceType()->isVoid()) { return false; @@ -1152,6 +1155,14 @@ NominalTypeDecl::getDistributedRemoteCallTargetInitFunction() const { GetDistributedRemoteCallTargetInitFunctionRequest(mutableThis), nullptr); } +ConstructorDecl * +NominalTypeDecl::getDistributedRemoteCallArgumentInitFunction() const { + auto mutableThis = const_cast(this); + return evaluateOrDefault( + getASTContext().evaluator, + GetDistributedRemoteCallArgumentInitFunctionRequest(mutableThis), nullptr); +} + AbstractFunctionDecl *ASTContext::getRemoteCallOnDistributedActorSystem( NominalTypeDecl *actorOrSystem, bool isVoidReturn) const { assert(actorOrSystem && "distributed actor (or system) decl must be provided"); diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 5f18152bf20e5..8fc5cea62a5a9 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -270,17 +270,66 @@ deriveBodyDistributed_thunk(AbstractFunctionDecl *thunk, void *context) { C, recordArgumentDecl->getName()); auto argumentName = param->getArgumentName().str(); - auto recordArgArgsList = ArgumentList::forImplicitCallTo( - recordArgumentDeclRef->getName(), + LiteralExpr *argumentLabelArg; + if (argumentName.empty()) { + argumentLabelArg = new (C) NilLiteralExpr(sloc, implicit); + } else { + argumentLabelArg = + new (C) StringLiteralExpr(argumentName, SourceRange(), implicit); + } + auto parameterName = param->getParameterName().str(); + + + // --- Prepare the RemoteCallArgument for the argument + auto argumentVarName = C.getIdentifier("_" + parameterName.str()); + StructDecl *RCA = C.getRemoteCallArgumentDecl(); + VarDecl *callArgVar = + new (C) VarDecl(/*isStatic=*/false, VarDecl::Introducer::Let, sloc, + argumentVarName, thunk); + callArgVar->setImplicit(); + callArgVar->setSynthesized(); + + Pattern *callArgPattern = NamedPattern::createImplicit(C, callArgVar); + + auto remoteCallArgumentInitDecl = + RCA->getDistributedRemoteCallArgumentInitFunction(); + auto boundRCAType = BoundGenericType::get( + RCA, Type(), {thunk->mapTypeIntoContext(param->getInterfaceType())}); + auto remoteCallArgumentInitDeclRef = + TypeExpr::createImplicit(boundRCAType, C); + + auto initCallArgArgs = ArgumentList::forImplicitCallTo( + DeclNameRef(remoteCallArgumentInitDecl->getEffectiveFullName()), { - // name: - new (C) StringLiteralExpr(argumentName, SourceRange(), - /*implicit=*/true), - // _ argument: - new (C) DeclRefExpr( + // label: + argumentLabelArg, + // name: + new (C) StringLiteralExpr(parameterName, SourceRange(), implicit), + // _ argument: + new (C) DeclRefExpr( ConcreteDeclRef(param), dloc, implicit, AccessSemantics::Ordinary, thunk->mapTypeIntoContext(param->getInterfaceType())) + }, + C); + + auto initCallArgCallExpr = + CallExpr::createImplicit(C, remoteCallArgumentInitDeclRef, initCallArgArgs); + initCallArgCallExpr->setImplicit(); + + auto callArgPB = PatternBindingDecl::createImplicit( + C, StaticSpellingKind::None, callArgPattern, initCallArgCallExpr, thunk); + + remoteBranchStmts.push_back(callArgPB); + remoteBranchStmts.push_back(callArgVar); + + /// --- Pass the argumentRepr to the recordArgument function + auto recordArgArgsList = ArgumentList::forImplicitCallTo( + recordArgumentDeclRef->getName(), + { + new (C) DeclRefExpr( + ConcreteDeclRef(callArgVar), dloc, implicit, + AccessSemantics::Ordinary) }, C); auto tryRecordArgExpr = TryExpr::createImplicit(C, sloc, diff --git a/lib/Sema/TypeCheckDistributed.cpp b/lib/Sema/TypeCheckDistributed.cpp index 5f681b3ddc01d..4a3397baf8062 100644 --- a/lib/Sema/TypeCheckDistributed.cpp +++ b/lib/Sema/TypeCheckDistributed.cpp @@ -305,7 +305,7 @@ bool swift::checkDistributedActorSystemAdHocProtocolRequirements( decl->getDescriptiveKind(), decl->getName(), identifier); decl->diagnose(diag::note_distributed_actor_system_conformance_missing_adhoc_requirement, decl->getName(), identifier, - "mutating func recordArgument(name: String, _ argument: Argument) throws\n"); + "mutating func recordArgument(_ argument: RemoteCallArgument) throws\n"); anyMissingAdHocRequirements = true; } if (checkAdHocRequirementAccessControl(decl, Proto, recordArgumentDecl)) @@ -731,6 +731,48 @@ GetDistributedRemoteCallTargetInitFunctionRequest::evaluate( return nullptr; } +ConstructorDecl* +GetDistributedRemoteCallArgumentInitFunctionRequest::evaluate( + Evaluator &evaluator, + NominalTypeDecl *nominal) const { + auto &C = nominal->getASTContext(); + + // not via `ensureDistributedModuleLoaded` to avoid generating a warning, + // we won't be emitting the offending decl after all. + if (!C.getLoadedModule(C.Id_Distributed)) + return nullptr; + + if (!nominal->getDeclaredInterfaceType()->isEqual( + C.getRemoteCallArgumentType())) + return nullptr; + + for (auto value : nominal->getMembers()) { + auto ctor = dyn_cast(value); + if (!ctor) + continue; + + auto params = ctor->getParameters(); + if (params->size() != 3) + return nullptr; + + // --- param: label + if (!params->get(0)->getArgumentName().is("label")) + return nullptr; + + // --- param: name + if (!params->get(1)->getArgumentName().is("name")) + return nullptr; + + // --- param: value + if (params->get(2)->getArgumentName() != C.Id_value) + return nullptr; + + return ctor; + } + + return nullptr; +} + NominalTypeDecl * GetDistributedActorInvocationDecoderRequest::evaluate(Evaluator &evaluator, NominalTypeDecl *actor) const { diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index 2b2ba6cddce9d..8cff96e4531b1 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -420,7 +420,9 @@ public protocol DistributedTargetInvocationEncoder { // /// // /// Record an argument of `Argument` type. // /// This will be invoked for every argument of the target, in declaration order. -// mutating func recordArgument(_ argument: Argument) throws +// mutating func recordArgument( +// _ argument: DistributedTargetArgument +// ) throws /// Record the error type of the distributed method. /// This method will not be invoked if the target is not throwing. @@ -435,8 +437,47 @@ public protocol DistributedTargetInvocationEncoder { mutating func doneRecording() throws } +/// Represents an argument passed to a distributed call target. @available(SwiftStdlib 5.7, *) -public +public struct RemoteCallArgument { + /// The "argument label" of the argument. + /// The label is the name visible name used in external calls made to this + /// target, e.g. for `func hello(label name: String)` it is `label`. + /// + /// If no label is specified (i.e. `func hi(name: String)`), the `label`, + /// value is empty, however `effectiveLabel` is equal to the `name`. + /// + /// In most situations, using `effectiveLabel` is more useful to identify + /// the user-visible name of this argument. + public let label: String? + + /// The effective label of this argument, i.e. if no explicit `label` was set + /// this defaults to the `name`. This reflects the semantics of call sites of + /// function declarations without explicit label definitions in Swift. + public var effectiveLabel: String { + return label ?? name + } + + /// The internal name of parameter this argument is accessible as in the + /// function body. It is not part of the functions API and may change without + /// breaking the target identifier. + /// + /// If the method did not declare an explicit `label`, it is used as the + /// `effectiveLabel`. + public let name: String + + /// The value of the argument being passed to the call. + /// As `RemoteCallArgument` is always used in conjunction with + /// `recordArgument` and populated by the compiler, this Value will generally + /// conform to a distributed actor system's `SerializationRequirement`. + public let value: Value + + public init(label: String?, name: String, value: Value) { + self.label = label + self.name = name + self.value = value + } +} /// Decoder that must be provided to `executeDistributedTarget` and is used /// by the Swift runtime to decode arguments of the invocation. diff --git a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift index 48502dcfdf21e..1de42b0570879 100644 --- a/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift +++ b/stdlib/public/Distributed/LocalTestingDistributedActorSystem.swift @@ -151,7 +151,7 @@ public struct LocalTestingInvocationEncoder: DistributedTargetInvocationEncoder fatalError("Attempted to call encoder method in a local-only actor system") } - public mutating func recordArgument(_ argument: Argument) throws { + public mutating func recordArgument(_ argument: RemoteCallArgument) throws { fatalError("Attempted to call encoder method in a local-only actor system") } diff --git a/test/Distributed/Inputs/BadDistributedActorSystems.swift b/test/Distributed/Inputs/BadDistributedActorSystems.swift index 4716262009115..2cef2ed0a25f9 100644 --- a/test/Distributed/Inputs/BadDistributedActorSystems.swift +++ b/test/Distributed/Inputs/BadDistributedActorSystems.swift @@ -257,8 +257,8 @@ public struct FakeInvocationEncoder : DistributedTargetInvocationEncoder { genericSubs.append(type) } - public mutating func recordArgument(name: String, _ argument: Argument) throws { - print(" > encode argument name:\(name), argument: \(argument)") + public mutating func recordArgument(_ argument: RemoteCallArgument) throws { + print(" > encode argument name:\(argument.effectiveLabel), argument: \(argument.value)") arguments.append(argument) } public mutating func recordErrorType(_ type: E.Type) throws { diff --git a/test/Distributed/Inputs/FakeDistributedActorSystems.swift b/test/Distributed/Inputs/FakeDistributedActorSystems.swift index 87c7e839d4876..8e9512e2d0f59 100644 --- a/test/Distributed/Inputs/FakeDistributedActorSystems.swift +++ b/test/Distributed/Inputs/FakeDistributedActorSystems.swift @@ -267,10 +267,10 @@ public struct FakeInvocationEncoder : DistributedTargetInvocationEncoder { genericSubs.append(type) } - public mutating func recordArgument( - name: String, _ argument: Argument) throws { - print(" > encode argument name:\(name), value: \(argument)") - arguments.append(argument) + public mutating func recordArgument( + _ argument: RemoteCallArgument) throws { + print(" > encode argument name:\(argument.label ?? "_"), value: \(argument.value)") + arguments.append(argument.value) } public mutating func recordErrorType(_ type: E.Type) throws { diff --git a/test/Distributed/Inputs/dynamic_replacement_da_decl.swift b/test/Distributed/Inputs/dynamic_replacement_da_decl.swift index 1e3aa0468e87f..368c45f168a8a 100644 --- a/test/Distributed/Inputs/dynamic_replacement_da_decl.swift +++ b/test/Distributed/Inputs/dynamic_replacement_da_decl.swift @@ -87,7 +87,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_decode.swift b/test/Distributed/Runtime/distributed_actor_decode.swift index 4902e144b88d5..a6344d73fb650 100644 --- a/test/Distributed/Runtime/distributed_actor_decode.swift +++ b/test/Distributed/Runtime/distributed_actor_decode.swift @@ -101,11 +101,11 @@ final class FakeActorSystem: DistributedActorSystem { } -class FakeInvocation: DistributedTargetInvocationEncoder, DistributedTargetInvocationDecoder { +final class FakeInvocation: DistributedTargetInvocationEncoder, DistributedTargetInvocationDecoder { typealias SerializationRequirement = Codable func recordGenericSubstitution(_ type: T.Type) throws {} - func recordArgument(name: String, _ argument: Argument) throws {} + func recordArgument(_ argument: RemoteCallArgument) throws {} func recordReturnType(_ type: R.Type) throws {} func recordErrorType(_ type: E.Type) throws {} func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_deinit.swift b/test/Distributed/Runtime/distributed_actor_deinit.swift index faae2e024f449..6c7dfbb152f1b 100644 --- a/test/Distributed/Runtime/distributed_actor_deinit.swift +++ b/test/Distributed/Runtime/distributed_actor_deinit.swift @@ -57,8 +57,8 @@ struct ActorAddress: Sendable, Hashable, Codable { final class FakeActorSystem: @unchecked Sendable, DistributedActorSystem { typealias ActorID = ActorAddress typealias SerializationRequirement = Codable - typealias InvocationDecoder = FakeDistributedInvocation - typealias InvocationEncoder = FakeDistributedInvocation + typealias InvocationDecoder = FakeDistributedInvocationEncoder + typealias InvocationEncoder = FakeDistributedInvocationEncoder var n = 0 @@ -119,11 +119,11 @@ final class FakeActorSystem: @unchecked Sendable, DistributedActorSystem { } } -class FakeDistributedInvocation: DistributedTargetInvocationEncoder, DistributedTargetInvocationDecoder { +class FakeDistributedInvocationEncoder: DistributedTargetInvocationEncoder, DistributedTargetInvocationDecoder { typealias SerializationRequirement = Codable func recordGenericSubstitution(_ type: T.Type) throws { } - func recordArgument(name: String, _ argument: Argument) throws { } + func recordArgument(_ argument: RemoteCallArgument) throws { } func recordReturnType(_ type: R.Type) throws { } func recordErrorType(_ type: E.Type) throws { } func doneRecording() throws { } diff --git a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift index 2f0649b59c739..aacd409c422d6 100644 --- a/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift +++ b/test/Distributed/Runtime/distributed_actor_func_calls_remoteCall_genericFunc.swift @@ -40,7 +40,7 @@ func test() async throws { let r1 = try await ref.generic("Caplin") // CHECK: > encode generic sub: Swift.String - // CHECK: > encode argument name:, value: Caplin + // CHECK: > encode argument name:_, value: Caplin // CHECK: > encode return type: Swift.String // CHECK: > done recording // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.generic(_:), invocation:FakeInvocationEncoder(genericSubs: [Swift.String], arguments: ["Caplin"], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String @@ -55,8 +55,8 @@ func test() async throws { // CHECK: > encode generic sub: Swift.String // CHECK: > encode generic sub: Swift.Int // CHECK: > encode argument name:strict, value: 2.0 - // CHECK: > encode argument name:, value: Caplin - // CHECK: > encode argument name:, value: [1, 2, 3] + // CHECK: > encode argument name:_, value: Caplin + // CHECK: > encode argument name:_, value: [1, 2, 3] // CHECK: > encode return type: Swift.String // CHECK: > done recording // CHECK: >> remoteCall: on:main.Greeter, target:main.Greeter.generic2(strict:_:_:), invocation:FakeInvocationEncoder(genericSubs: [Swift.String, Swift.Int], arguments: [2.0, "Caplin", [1, 2, 3]], returnType: Optional(Swift.String), errorType: nil), throwing:Swift.Never, returning:Swift.String diff --git a/test/Distributed/Runtime/distributed_actor_init_local.swift b/test/Distributed/Runtime/distributed_actor_init_local.swift index f8cb6432144e3..39d07005d3bf8 100644 --- a/test/Distributed/Runtime/distributed_actor_init_local.swift +++ b/test/Distributed/Runtime/distributed_actor_init_local.swift @@ -126,7 +126,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_remoteCall.swift b/test/Distributed/Runtime/distributed_actor_remoteCall.swift index a36906a71fca0..cdb2054a08998 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCall.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCall.swift @@ -182,8 +182,8 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { mutating func recordGenericSubstitution(_ type: T.Type) throws { substitutions.append(type) } - mutating func recordArgument(name: String, _ argument: Argument) throws { - arguments.append(argument) + mutating func recordArgument(_ argument: RemoteCallArgument) throws { + arguments.append(argument.value) } mutating func recordErrorType(_ type: E.Type) throws { self.errorType = type @@ -343,8 +343,8 @@ func test() async throws { // CHECK: RETURN: bar var echoInvocation = system.makeInvocationEncoder() - try echoInvocation.recordArgument(name: "argument-name", "Caplin") - try echoInvocation.recordArgument(name: "argument-name", 42) + try echoInvocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: "Caplin")) + try echoInvocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: 42)) try echoInvocation.doneRecording() var echoDecoder = echoInvocation.makeDecoder() @@ -359,7 +359,7 @@ func test() async throws { var generic1Invocation = system.makeInvocationEncoder() try generic1Invocation.recordGenericSubstitution(Int.self) - try generic1Invocation.recordArgument(name: "argument-name", 42) + try generic1Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: 42)) try generic1Invocation.doneRecording() var generic1Decoder = generic1Invocation.makeDecoder() @@ -376,8 +376,8 @@ func test() async throws { try generic2Invocation.recordGenericSubstitution(Int.self) try generic2Invocation.recordGenericSubstitution(String.self) - try generic2Invocation.recordArgument(name: "argument-name", 42) - try generic2Invocation.recordArgument(name: "argument-name", "Ultimate Question!") + try generic2Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: 42)) + try generic2Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: "Ultimate Question!")) try generic2Invocation.doneRecording() var generic2Decoder = generic2Invocation.makeDecoder() @@ -396,9 +396,9 @@ func test() async throws { try generic3Invocation.recordGenericSubstitution(Int.self) try generic3Invocation.recordGenericSubstitution(String.self) try generic3Invocation.recordGenericSubstitution(S.self) - try generic3Invocation.recordArgument(name: "argument-name", 42) - try generic3Invocation.recordArgument(name: "argument-name", ["a", "b", "c"]) - try generic3Invocation.recordArgument(name: "argument-name", S(data: 42)) + try generic3Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: 42)) + try generic3Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: ["a", "b", "c"])) + try generic3Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: S(data: 42))) try generic3Invocation.doneRecording() var generic3Decoder = generic3Invocation.makeDecoder() @@ -418,9 +418,9 @@ func test() async throws { try generic4Invocation.recordGenericSubstitution(Int.self) try generic4Invocation.recordGenericSubstitution(Int.self) try generic4Invocation.recordGenericSubstitution(String.self) - try generic4Invocation.recordArgument(name: "argument-name", 42) - try generic4Invocation.recordArgument(name: "argument-name", S(data: 42)) - try generic4Invocation.recordArgument(name: "argument-name", ["a", "b", "c"]) + try generic4Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: 42)) + try generic4Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: S(data: 42))) + try generic4Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: ["a", "b", "c"])) try generic4Invocation.doneRecording() var generic4Decoder = generic4Invocation.makeDecoder() @@ -441,10 +441,10 @@ func test() async throws { try generic5Invocation.recordGenericSubstitution(Int.self) try generic5Invocation.recordGenericSubstitution(String.self) try generic5Invocation.recordGenericSubstitution([Int].self) - try generic5Invocation.recordArgument(name: "argument-name", 42) - try generic5Invocation.recordArgument(name: "argument-name", S(data: 42)) - try generic5Invocation.recordArgument(name: "argument-name", "Hello, World!") - try generic5Invocation.recordArgument(name: "argument-name", [0, 42]) + try generic5Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: 42)) + try generic5Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: S(data: 42))) + try generic5Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: "Hello, World!")) + try generic5Invocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: [0, 42])) try generic5Invocation.doneRecording() var generic5Decoder = generic5Invocation.makeDecoder() @@ -467,7 +467,7 @@ func test() async throws { var genericOptInvocation = system.makeInvocationEncoder() try genericOptInvocation.recordGenericSubstitution([Int].self) - try genericOptInvocation.recordArgument(name: "argument-name", [0, 42]) + try genericOptInvocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: [0, 42])) try genericOptInvocation.doneRecording() var genericOptDecoder = genericOptInvocation.makeDecoder() @@ -482,7 +482,7 @@ func test() async throws { var decodeErrInvocation = system.makeInvocationEncoder() - try decodeErrInvocation.recordArgument(name: "argument-name", 42) + try decodeErrInvocation.recordArgument(RemoteCallArgument(label: "argument-name", name: "argument-name", value: 42)) try decodeErrInvocation.doneRecording() var decodeErrDecoder = decodeErrInvocation.makeDecoder() diff --git a/test/Distributed/Runtime/distributed_actor_remote_functions.swift b/test/Distributed/Runtime/distributed_actor_remote_functions.swift index a613f061171a4..8fdd824b838cf 100644 --- a/test/Distributed/Runtime/distributed_actor_remote_functions.swift +++ b/test/Distributed/Runtime/distributed_actor_remote_functions.swift @@ -153,7 +153,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift b/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift index 6bbc8dd24573c..c7c282ab6ea2a 100644 --- a/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift +++ b/test/Distributed/Runtime/distributed_actor_remote_retains_system.swift @@ -101,7 +101,7 @@ class FakeInvocation: DistributedTargetInvocationEncoder, DistributedTargetInvoc typealias SerializationRequirement = Codable func recordGenericSubstitution(_ type: T.Type) throws {} - func recordArgument(name: String, _ argument: Argument) throws {} + func recordArgument(_ argument: RemoteCallArgument) throws {} func recordReturnType(_ type: R.Type) throws {} func recordErrorType(_ type: E.Type) throws {} func doneRecording() throws {} diff --git a/test/Distributed/Runtime/distributed_actor_self_calls.swift b/test/Distributed/Runtime/distributed_actor_self_calls.swift index 0eccf8c7d139b..805bc8d4e65f2 100644 --- a/test/Distributed/Runtime/distributed_actor_self_calls.swift +++ b/test/Distributed/Runtime/distributed_actor_self_calls.swift @@ -99,7 +99,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} diff --git a/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift b/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift index d69e3d8efa7b2..d3e144a0c7a4f 100644 --- a/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift +++ b/test/Distributed/distributed_actor_system_missing_adhoc_requirement_impls.swift @@ -500,7 +500,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -510,7 +510,7 @@ struct AnyInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Any mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -520,7 +520,7 @@ struct LargeSerializationReqFakeInvocationEncoder: DistributedTargetInvocationEn typealias SerializationRequirement = Codable & SomeProtocol mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -530,7 +530,7 @@ public struct PublicFakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(name: String, _ argument: Argument) throws {} + public mutating func recordArgument(_ argument: RemoteCallArgument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} @@ -542,7 +542,7 @@ struct FakeInvocationEncoder_missing_recordArgument: DistributedTargetInvocation typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - // MISSING: mutating func recordArgument(name: String, _ argument: Argument) throws {} + // MISSING: mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -566,7 +566,7 @@ struct FakeInvocationEncoder_missing_recordReturnType: DistributedTargetInvocati typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} // MISSING: mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -577,7 +577,7 @@ struct FakeInvocationEncoder_missing_recordErrorType: DistributedTargetInvocatio typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} // MISSING: mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} @@ -596,6 +596,17 @@ struct FakeInvocationEncoder_recordArgument_wrongType: DistributedTargetInvocati mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} } +struct FakeInvocationEncoder_recordArgument_missingMutating: DistributedTargetInvocationEncoder { + //expected-error@-1{{struct 'FakeInvocationEncoder_recordArgument_missingMutating' is missing witness for protocol requirement 'recordArgument'}} + //expected-note@-2{{protocol 'FakeInvocationEncoder_recordArgument_missingMutating' requires function 'recordArgument' with signature:}} + typealias SerializationRequirement = Codable + + mutating func recordGenericSubstitution(_ type: T.Type) throws {} + func recordArgument(_ argument: RemoteCallArgument) throws {} + mutating func recordReturnType(_ type: R.Type) throws {} + mutating func recordErrorType(_ type: E.Type) throws {} + mutating func doneRecording() throws {} +} struct FakeInvocationEncoder_recordResultType_wrongType: DistributedTargetInvocationEncoder { //expected-error@-1{{struct 'FakeInvocationEncoder_recordResultType_wrongType' is missing witness for protocol requirement 'recordReturnType'}} @@ -603,7 +614,7 @@ struct FakeInvocationEncoder_recordResultType_wrongType: DistributedTargetInvoca typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(s: String, _ resultType: R.Type) throws {} // BAD mutating func recordReturnType(_ resultType: R.Type) throws {} // BAD mutating func recordReturnType(badName: R.Type) throws {} // BAD @@ -616,7 +627,7 @@ struct FakeInvocationEncoder_recordErrorType_wrongType: DistributedTargetInvocat typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(BadName type: E.Type) throws {} // BAD mutating func recordErrorType(_ type: E.Type) throws {} // BAD diff --git a/test/IRGen/distributed_actor.swift b/test/IRGen/distributed_actor.swift index 40a7c379eb861..3d79b94d9de08 100644 --- a/test/IRGen/distributed_actor.swift +++ b/test/IRGen/distributed_actor.swift @@ -102,7 +102,7 @@ public struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(name: String, _ argument: Argument) throws {} + public mutating func recordArgument(_ argument: RemoteCallArgument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} diff --git a/test/SILGen/distributed_thunk.swift b/test/SILGen/distributed_thunk.swift index d574ef53392b7..a712879190cbd 100644 --- a/test/SILGen/distributed_thunk.swift +++ b/test/SILGen/distributed_thunk.swift @@ -106,7 +106,7 @@ public struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(name: String, _ argument: Argument) throws {} + public mutating func recordArgument(_ argument: RemoteCallArgument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} diff --git a/test/Serialization/Inputs/def_distributed.swift b/test/Serialization/Inputs/def_distributed.swift index 8b75215acaa08..2c401dd44c445 100644 --- a/test/Serialization/Inputs/def_distributed.swift +++ b/test/Serialization/Inputs/def_distributed.swift @@ -99,7 +99,7 @@ public struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(name: String, _ argument: Argument) throws {} + public mutating func recordArgument(_ argument: RemoteCallArgument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} diff --git a/test/TBD/distributed.swift b/test/TBD/distributed.swift index 4807412561d17..3eb079c3df344 100644 --- a/test/TBD/distributed.swift +++ b/test/TBD/distributed.swift @@ -115,7 +115,7 @@ public struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { public typealias SerializationRequirement = Codable public mutating func recordGenericSubstitution(_ type: T.Type) throws {} - public mutating func recordArgument(name: String, _ argument: Argument) throws {} + public mutating func recordArgument(_ argument: RemoteCallArgument) throws {} public mutating func recordReturnType(_ type: R.Type) throws {} public mutating func recordErrorType(_ type: E.Type) throws {} public mutating func doneRecording() throws {} diff --git a/test/decl/protocol/special/DistributedActor.swift b/test/decl/protocol/special/DistributedActor.swift index adb8202fd06b9..85841ec084e02 100644 --- a/test/decl/protocol/special/DistributedActor.swift +++ b/test/decl/protocol/special/DistributedActor.swift @@ -161,7 +161,7 @@ struct FakeInvocationEncoder: DistributedTargetInvocationEncoder { typealias SerializationRequirement = Codable mutating func recordGenericSubstitution(_ type: T.Type) throws {} - mutating func recordArgument(name: String, _ argument: Argument) throws {} + mutating func recordArgument(_ argument: RemoteCallArgument) throws {} mutating func recordReturnType(_ type: R.Type) throws {} mutating func recordErrorType(_ type: E.Type) throws {} mutating func doneRecording() throws {} From f538d33e5f56a32c3688070917ff6731e717729f Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 17 Mar 2022 09:06:47 +0100 Subject: [PATCH 175/242] [CodeCompletion][Sema] Migrate CallArgurment position completion to the solver-based implementation This hooks up call argument position completion to the typeCheckForCodeCompletion API to generate completions from all the solutions the constraint solver produces (even those requiring fixes), rather than relying on a single solution being applied to the AST (if any). Co-authored-by: Nathan Hawes --- include/swift/IDE/ArgumentCompletion.h | 79 ++++ include/swift/IDE/CodeCompletion.h | 4 + include/swift/IDE/PossibleParamInfo.h | 15 + include/swift/Sema/ConstraintSystem.h | 59 ++- lib/IDE/ArgumentCompletion.cpp | 285 +++++++++++++ lib/IDE/CMakeLists.txt | 1 + lib/IDE/CodeCompletion.cpp | 76 +--- lib/Sema/BuilderTransform.cpp | 7 + lib/Sema/CSBindings.cpp | 7 + lib/Sema/CSClosure.cpp | 4 +- lib/Sema/CSGen.cpp | 89 +++- lib/Sema/CSRanking.cpp | 15 +- lib/Sema/CSSimplify.cpp | 270 ++++++++---- lib/Sema/ConstraintSystem.cpp | 62 ++- lib/Sema/TypeCheckCodeCompletion.cpp | 11 +- test/IDE/complete_ambiguous.swift | 104 +++++ test/IDE/complete_call_arg.swift | 386 +++++++++++++++++- ..._enum_unresolved_dot_argument_labels.swift | 54 ++- test/IDE/complete_subscript.swift | 36 +- test/IDE/complete_swift_key_path.swift | 4 +- test/IDE/complete_unresolved_members.swift | 13 +- ...completion-type-not-part-of-solution.swift | 17 + .../0027-autoclosure-curry-thunk.swift | 26 ++ .../0028-member-reference-error-type.swift | 9 + .../0029-closure-implicit-return.swift | 32 ++ .../0030-arg-completion-no-locator.swift | 15 + ...2-constructor-call-in-result-builder.swift | 15 + ...ptimize-constraints-for-ignored-args.swift | 7 + ...-trailing-closure-arg-label-matching.swift | 11 + 29 files changed, 1532 insertions(+), 181 deletions(-) create mode 100644 include/swift/IDE/ArgumentCompletion.h create mode 100644 lib/IDE/ArgumentCompletion.cpp create mode 100644 validation-test/IDE/crashers_2_fixed/0026-completion-type-not-part-of-solution.swift create mode 100644 validation-test/IDE/crashers_2_fixed/0027-autoclosure-curry-thunk.swift create mode 100644 validation-test/IDE/crashers_2_fixed/0028-member-reference-error-type.swift create mode 100644 validation-test/IDE/crashers_2_fixed/0029-closure-implicit-return.swift create mode 100644 validation-test/IDE/crashers_2_fixed/0030-arg-completion-no-locator.swift create mode 100644 validation-test/IDE/crashers_2_fixed/0032-constructor-call-in-result-builder.swift create mode 100644 validation-test/IDE/crashers_2_fixed/0033-dont-optimize-constraints-for-ignored-args.swift create mode 100644 validation-test/IDE/crashers_2_fixed/0033-trailing-closure-arg-label-matching.swift diff --git a/include/swift/IDE/ArgumentCompletion.h b/include/swift/IDE/ArgumentCompletion.h new file mode 100644 index 0000000000000..c6b9d404c8b8e --- /dev/null +++ b/include/swift/IDE/ArgumentCompletion.h @@ -0,0 +1,79 @@ +//===--- ArgumentCompletion.h -----------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_ARGUMENTCOMPLETION_H +#define SWIFT_IDE_ARGUMENTCOMPLETION_H + +#include "swift/IDE/CodeCompletionConsumer.h" +#include "swift/IDE/CodeCompletionContext.h" +#include "swift/IDE/PossibleParamInfo.h" +#include "swift/Sema/CodeCompletionTypeChecking.h" + +namespace swift { +namespace ide { + +class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback { + struct Result { + /// The type associated with the code completion expression itself. + Type ExpectedType; + /// True if this is a subscript rather than a function call. + bool IsSubscript; + /// The FuncDecl or SubscriptDecl associated with the call. + ValueDecl *FuncD; + /// The type of the function being called. + Type FuncTy; + /// The index of the argument containing the completion location + unsigned ArgIdx; + /// The index of the parameter corresponding to the completion argument. + Optional ParamIdx; + /// The indices of all params that were bound to non-synthesized + /// arguments. Used so we don't suggest them even when the args are out of + /// order. + std::set ClaimedParamIndices; + /// True if the completion is a noninitial term in a variadic argument. + bool IsNoninitialVariadic; + /// The base type of the call/subscript (null for free functions). + Type BaseType; + /// True if an argument label precedes the completion location. + bool HasLabel; + }; + + CodeCompletionExpr *CompletionExpr; + SmallVector Results; + + /// Populates a vector of parameters to suggest along with a vector of types + /// to match the lookup results against. + /// + /// \Returns true if global lookup should be performed. + bool addPossibleParams(const ArgumentTypeCheckCompletionCallback::Result &Res, + SmallVectorImpl &Params, + SmallVectorImpl &Types); + +public: + ArgumentTypeCheckCompletionCallback(CodeCompletionExpr *CompletionExpr) + : CompletionExpr(CompletionExpr) {} + + void sawSolution(const constraints::Solution &solution) override; + + /// \param IncludeSignature Whether to include a suggestion for the entire + /// function signature instead of suggesting individual labels. Used when + /// completing after the opening '(' of a function call \param Loc The + /// location of the code completion token + void deliverResults(bool IncludeSignature, SourceLoc Loc, DeclContext *DC, + CodeCompletionContext &CompletionCtx, + CodeCompletionConsumer &Consumer); +}; + +} // end namespace ide +} // end namespace swift + +#endif // SWIFT_IDE_ARGUMENTCOMPLETION_H diff --git a/include/swift/IDE/CodeCompletion.h b/include/swift/IDE/CodeCompletion.h index 4d5522a865be8..8a8a4df0a97a1 100644 --- a/include/swift/IDE/CodeCompletion.h +++ b/include/swift/IDE/CodeCompletion.h @@ -84,6 +84,10 @@ void lookupCodeCompletionResultsFromModule(CodeCompletionResultSink &targetSink, bool needLeadingDot, const SourceFile *SF); +void addExprKeywords(CodeCompletionResultSink &Sink, DeclContext *DC); + +void addSuperKeyword(CodeCompletionResultSink &Sink, DeclContext *DC); + } // end namespace ide } // end namespace swift diff --git a/include/swift/IDE/PossibleParamInfo.h b/include/swift/IDE/PossibleParamInfo.h index 429986e1075ff..dcc82ebda6921 100644 --- a/include/swift/IDE/PossibleParamInfo.h +++ b/include/swift/IDE/PossibleParamInfo.h @@ -33,6 +33,21 @@ struct PossibleParamInfo { assert((Param || !IsRequired) && "nullptr with required flag is not allowed"); }; + + friend bool operator==(const PossibleParamInfo &lhs, + const PossibleParamInfo &rhs) { + bool ParamsMatch; + if (lhs.Param == nullptr && rhs.Param == nullptr) { + ParamsMatch = true; + } else if (lhs.Param == nullptr || rhs.Param == nullptr) { + // One is nullptr but the other is not. + ParamsMatch = false; + } else { + // Both are not nullptr. + ParamsMatch = (*lhs.Param == *rhs.Param); + } + return ParamsMatch && (lhs.IsRequired == rhs.IsRequired); + } }; } // end namespace ide diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index ad9f62b72c835..81a9bb32d59a4 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -2406,6 +2406,10 @@ class ConstraintSystem { /// diagnostics when result builder has multiple overloads. llvm::SmallDenseSet InvalidResultBuilderBodies; + /// Arguments after the code completion token that were thus ignored (i.e. + /// assigned fresh type variables) for type checking. + llvm::SetVector IgnoredArguments; + /// Maps node types used within all portions of the constraint /// system, instead of directly using the types on the /// nodes themselves. This allows us to typecheck and @@ -3171,9 +3175,24 @@ class ConstraintSystem { return TypeVariables.count(typeVar) > 0; } - /// Whether the given expression's source range contains the code + /// Whether the given ASTNode's source range contains the code /// completion location. - bool containsCodeCompletionLoc(Expr *expr) const; + bool containsCodeCompletionLoc(ASTNode node) const; + bool containsCodeCompletionLoc(const ArgumentList *args) const; + + /// Marks the argument with the \p ArgLoc locator as being ignored because it + /// occurs after the code completion token. This assumes that the argument is + /// not type checked (by assigning it a fresh type variable) and prevents + /// fixes from being generated for this argument. + void markArgumentIgnoredForCodeCompletion(ConstraintLocator *ArgLoc) { + IgnoredArguments.insert(ArgLoc); + } + + /// Whether the argument with the \p ArgLoc locator occurs after the code + /// completion tokena and thus should be ignored and not generate any fixes. + bool isArgumentIgnoredForCodeCompletion(ConstraintLocator *ArgLoc) { + return IgnoredArguments.count(ArgLoc) > 0; + } void setClosureType(const ClosureExpr *closure, FunctionType *type) { assert(closure); @@ -5534,8 +5553,44 @@ class MatchCallArgumentListener { /// \returns true to indicate that this should cause a failure, false /// otherwise. virtual bool relabelArguments(ArrayRef newNames); + + /// \returns true if matchCallArguments should try to claim the argument at + /// \p argIndex while recovering from a failure. This is used to prevent + /// claiming of arguments after the code completion token. + virtual bool shouldClaimArgDuringRecovery(unsigned argIdx); + + /// \returns true if \p arg can be claimed even though its argument label + /// doesn't match. This is the case for arguments representing the code + /// completion token if they don't contain a label. In these cases completion + /// will suggest the label. + virtual bool + canClaimArgIgnoringNameMismatch(const AnyFunctionType::Param &arg); +}; + +/// For a callsite containing a code completion expression, stores the index of +/// the arg containing it along with the index of the first trailing closure and +/// how many arguments were passed in total. +struct CompletionArgInfo { + unsigned completionIdx; + Optional firstTrailingIdx; + unsigned argCount; + + /// \returns true if the given argument index is possibly about to be written + /// by the user (given the completion index) so shouldn't be penalised as + /// missing when ranking solutions. + bool allowsMissingArgAt(unsigned argInsertIdx, AnyFunctionType::Param param); + + /// \returns true if the argument containing the completion location is before + /// the argument with the given index. + bool isBefore(unsigned argIdx) { return completionIdx < argIdx; } }; +/// Extracts the index of the argument containing the code completion location +/// from the provided anchor if it's a \c CallExpr, \c SubscriptExpr, or +/// \c ObjectLiteralExpr. +Optional getCompletionArgInfo(ASTNode anchor, + ConstraintSystem &cs); + /// Match the call arguments (as described by the given argument type) to /// the parameters (as described by the given parameter type). /// diff --git a/lib/IDE/ArgumentCompletion.cpp b/lib/IDE/ArgumentCompletion.cpp new file mode 100644 index 0000000000000..d019522ddfefe --- /dev/null +++ b/lib/IDE/ArgumentCompletion.cpp @@ -0,0 +1,285 @@ +//===--- ArgumentCompletion.cpp ---------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/IDE/ArgumentCompletion.h" +#include "swift/IDE/CodeCompletion.h" +#include "swift/IDE/CompletionLookup.h" +#include "swift/Sema/ConstraintSystem.h" +#include "swift/Sema/IDETypeChecking.h" + +using namespace swift; +using namespace swift::ide; +using namespace swift::constraints; + +/// Returns true if both types are null or if they are equal. +static bool nullableTypesEqual(Type LHS, Type RHS) { + if (LHS.isNull() && RHS.isNull()) { + return true; + } else if (LHS.isNull() || RHS.isNull()) { + // One type is null but the other is not. + return false; + } else { + return LHS->isEqual(RHS); + } +} + +bool ArgumentTypeCheckCompletionCallback::addPossibleParams( + const ArgumentTypeCheckCompletionCallback::Result &Res, + SmallVectorImpl &Params, SmallVectorImpl &Types) { + if (!Res.ParamIdx) { + // We don't really know much here. Suggest global results without a specific + // expected type. + return true; + } + + if (Res.HasLabel) { + // We already have a parameter label, suggest types + Types.push_back(Res.ExpectedType); + return true; + } + + ArrayRef ParamsToPass = + Res.FuncTy->getAs()->getParams(); + + ParameterList *PL = nullptr; + if (Res.FuncD) { + PL = swift::getParameterList(Res.FuncD); + } + assert(!PL || PL->size() == ParamsToPass.size()); + + bool ShowGlobalCompletions = false; + for (auto Idx : range(*Res.ParamIdx, ParamsToPass.size())) { + bool IsCompletion = (Idx == Res.ParamIdx); + + // Stop at the first param claimed by other arguments. + if (!IsCompletion && Res.ClaimedParamIndices.count(Idx) > 0) { + break; + } + + const AnyFunctionType::Param *P = &ParamsToPass[Idx]; + bool Required = + !(PL && PL->get(Idx)->isDefaultArgument()) && !P->isVariadic(); + + if (P->hasLabel() && !(IsCompletion && Res.IsNoninitialVariadic)) { + // Suggest parameter label if parameter has label, we are completing in it + // and it is not a variadic parameter that already has arguments + PossibleParamInfo PP(P, Required); + if (!llvm::is_contained(Params, PP)) { + Params.push_back(std::move(PP)); + } + } else { + // We have a parameter that doesn't require a label. Suggest global + // results for that type. + ShowGlobalCompletions = true; + Types.push_back(P->getPlainType()); + } + if (Required) { + // The user should only be suggested the first required param. Stop. + break; + } + } + return ShowGlobalCompletions; +} + +void ArgumentTypeCheckCompletionCallback::sawSolution(const Solution &S) { + TypeCheckCompletionCallback::sawSolution(S); + + Type ExpectedTy = getTypeForCompletion(S, CompletionExpr); + if (!ExpectedTy) { + return; + } + + auto &CS = S.getConstraintSystem(); + + Expr *ParentCall = CompletionExpr; + while (ParentCall && ParentCall->getArgs() == nullptr) { + ParentCall = CS.getParentExpr(ParentCall); + } + + if (!ParentCall || ParentCall == CompletionExpr) { + assert(false && "no containing call?"); + return; + } + + auto ArgInfo = getCompletionArgInfo(ParentCall, CS); + if (!ArgInfo) { + assert(false && "bad parent call match?"); + return; + } + auto ArgIdx = ArgInfo->completionIdx; + + auto *CallLocator = CS.getConstraintLocator(ParentCall); + auto *CalleeLocator = S.getCalleeLocator(CallLocator); + auto SelectedOverload = S.getOverloadChoiceIfAvailable(CalleeLocator); + if (!SelectedOverload) { + return; + } + + Type CallBaseTy = SelectedOverload->choice.getBaseType(); + if (CallBaseTy) { + CallBaseTy = S.simplifyType(CallBaseTy)->getRValueType(); + } + + ValueDecl *FuncD = SelectedOverload->choice.getDeclOrNull(); + Type FuncTy = S.simplifyType(SelectedOverload->openedType)->getRValueType(); + + // For completion as the arg in a call to the implicit [keypath: _] subscript + // the solver can't know what kind of keypath is expected without an actual + // argument (e.g. a KeyPath vs WritableKeyPath) so it ends up as a hole. + // Just assume KeyPath so we show the expected keypath's root type to users + // rather than '_'. + if (SelectedOverload->choice.getKind() == + OverloadChoiceKind::KeyPathApplication) { + auto Params = FuncTy->getAs()->getParams(); + if (Params.size() == 1 && Params[0].getPlainType()->is()) { + auto *KPDecl = CS.getASTContext().getKeyPathDecl(); + Type KPTy = + KPDecl->mapTypeIntoContext(KPDecl->getDeclaredInterfaceType()); + Type KPValueTy = KPTy->castTo()->getGenericArgs()[1]; + KPTy = BoundGenericType::get(KPDecl, Type(), {CallBaseTy, KPValueTy}); + FuncTy = FunctionType::get({Params[0].withType(KPTy)}, KPValueTy); + } + } + + // Find the parameter the completion was bound to (if any), as well as which + // parameters are already bound (so we don't suggest them even when the args + // are out of order). + Optional ParamIdx; + std::set ClaimedParams; + bool IsNoninitialVariadic = false; + + ConstraintLocator *ArgumentLocator; + ArgumentLocator = + CS.getConstraintLocator(CallLocator, ConstraintLocator::ApplyArgument); + auto ArgMatchChoices = S.argumentMatchingChoices.find(ArgumentLocator); + if (ArgMatchChoices != S.argumentMatchingChoices.end()) { + // We might not have argument matching choices when applying a subscript + // found via @dynamicMemberLookup. + auto Bindings = ArgMatchChoices->second.parameterBindings; + + for (auto i : indices(Bindings)) { + bool Claimed = false; + for (auto j : Bindings[i]) { + if (j == ArgIdx) { + assert(!ParamIdx); + ParamIdx = i; + IsNoninitialVariadic = llvm::any_of( + Bindings[i], [j](unsigned other) { return other < j; }); + } + // Synthesized args don't count. + if (j < ArgInfo->argCount) { + Claimed = true; + } + } + if (Claimed) { + ClaimedParams.insert(i); + } + } + } else { + // FIXME: We currently don't look through @dynamicMemberLookup applications + // for subscripts (rdar://90363138) + } + + bool HasLabel = false; + if (auto PE = CS.getParentExpr(CompletionExpr)) { + if (auto Args = PE->getArgs()) { + HasLabel = !Args->getLabel(ArgIdx).empty(); + } + } + + // If this is a duplicate of any other result, ignore this solution. + if (llvm::any_of(Results, [&](const Result &R) { + return R.FuncD == FuncD && nullableTypesEqual(R.FuncTy, FuncTy) && + nullableTypesEqual(R.BaseType, CallBaseTy) && + R.ParamIdx == ParamIdx && + R.IsNoninitialVariadic == IsNoninitialVariadic; + })) { + return; + } + + Results.push_back({ExpectedTy, isa(ParentCall), FuncD, FuncTy, + ArgIdx, ParamIdx, std::move(ClaimedParams), + IsNoninitialVariadic, CallBaseTy, HasLabel}); +} + +void ArgumentTypeCheckCompletionCallback::deliverResults( + bool IncludeSignature, SourceLoc Loc, DeclContext *DC, + ide::CodeCompletionContext &CompletionCtx, + CodeCompletionConsumer &Consumer) { + ASTContext &Ctx = DC->getASTContext(); + CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC, + &CompletionCtx); + + // Perform global completion as a fallback if we don't have any results. + bool shouldPerformGlobalCompletion = Results.empty(); + SmallVector ExpectedTypes; + + if (IncludeSignature && !Results.empty()) { + Lookup.setHaveLParen(true); + for (auto &Result : Results) { + auto SemanticContext = SemanticContextKind::None; + NominalTypeDecl *BaseNominal = nullptr; + if (Result.BaseType) { + Type BaseTy = Result.BaseType; + if (auto InstanceTy = BaseTy->getMetatypeInstanceType()) { + BaseTy = InstanceTy; + } + if ((BaseNominal = BaseTy->getAnyNominal())) { + SemanticContext = SemanticContextKind::CurrentNominal; + if (Result.FuncD && + Result.FuncD->getDeclContext()->getSelfNominalTypeDecl() != + BaseNominal) { + SemanticContext = SemanticContextKind::Super; + } + } else if (BaseTy->is() || BaseTy->is()) { + SemanticContext = SemanticContextKind::CurrentNominal; + } + } + if (Result.IsSubscript) { + assert(SemanticContext != SemanticContextKind::None); + auto *SD = dyn_cast_or_null(Result.FuncD); + Lookup.addSubscriptCallPattern(Result.FuncTy->getAs(), + SD, SemanticContext); + } else { + auto *FD = dyn_cast_or_null(Result.FuncD); + Lookup.addFunctionCallPattern(Result.FuncTy->getAs(), + FD, SemanticContext); + } + } + Lookup.setHaveLParen(false); + + shouldPerformGlobalCompletion |= + !Lookup.FoundFunctionCalls || Lookup.FoundFunctionsWithoutFirstKeyword; + } else if (!Results.empty()) { + SmallVector Params; + for (auto &Ret : Results) { + shouldPerformGlobalCompletion |= + addPossibleParams(Ret, Params, ExpectedTypes); + } + Lookup.addCallArgumentCompletionResults(Params); + } + + if (shouldPerformGlobalCompletion) { + for (auto &Result : Results) { + ExpectedTypes.push_back(Result.ExpectedType); + } + Lookup.setExpectedTypes(ExpectedTypes, false); + Lookup.getValueCompletionsInDeclContext(Loc); + Lookup.getSelfTypeCompletionInDeclContext(Loc, /*isForDeclResult=*/false); + + // Add any keywords that can be used in an argument expr position. + addSuperKeyword(CompletionCtx.getResultSink(), DC); + addExprKeywords(CompletionCtx.getResultSink(), DC); + } + + deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer); +} diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index 94c727d02480f..83d79f787d236 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -1,5 +1,6 @@ add_swift_host_library(swiftIDE STATIC + ArgumentCompletion.cpp CodeCompletion.cpp CodeCompletionCache.cpp CodeCompletionContext.cpp diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 17c291472e03d..52e99ebc8db40 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -32,6 +32,7 @@ #include "swift/ClangImporter/ClangImporter.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Frontend/FrontendOptions.h" +#include "swift/IDE/ArgumentCompletion.h" #include "swift/IDE/CodeCompletionCache.h" #include "swift/IDE/CodeCompletionConsumer.h" #include "swift/IDE/CodeCompletionResultPrinter.h" @@ -830,7 +831,8 @@ static void addObserverKeywords(CodeCompletionResultSink &Sink) { addKeyword(Sink, "didSet", CodeCompletionKeywordKind::None); } -static void addExprKeywords(CodeCompletionResultSink &Sink, DeclContext *DC) { +void swift::ide::addExprKeywords(CodeCompletionResultSink &Sink, + DeclContext *DC) { // Expression is invalid at top-level of non-script files. CodeCompletionFlair flair; if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) { @@ -844,7 +846,8 @@ static void addExprKeywords(CodeCompletionResultSink &Sink, DeclContext *DC) { addKeyword(Sink, "await", CodeCompletionKeywordKind::None, "", flair); } -static void addSuperKeyword(CodeCompletionResultSink &Sink, DeclContext *DC) { +void swift::ide::addSuperKeyword(CodeCompletionResultSink &Sink, + DeclContext *DC) { if (!DC) return; auto *TC = DC->getInnermostTypeContext(); @@ -1373,6 +1376,22 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { Lookup.deliverResults(CurDeclContext, DotLoc, CompletionContext, Consumer); return true; } + case CompletionKind::CallArg: { + assert(CodeCompleteTokenExpr); + assert(CurDeclContext); + ArgumentTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr); + llvm::SaveAndRestore CompletionCollector( + Context.CompletionCallback, &Lookup); + typeCheckContextAt(CurDeclContext, CompletionLoc); + + if (!Lookup.gotCallback()) { + Lookup.fallbackTypeCheck(CurDeclContext); + } + + Lookup.deliverResults(ShouldCompleteCallPatternAfterParen, CompletionLoc, + CurDeclContext, CompletionContext, Consumer); + return true; + } default: return false; } @@ -1494,6 +1513,7 @@ void CodeCompletionCallbacksImpl::doneParsing() { case CompletionKind::DotExpr: case CompletionKind::UnresolvedMember: case CompletionKind::KeyPathExprSwift: + case CompletionKind::CallArg: llvm_unreachable("should be already handled"); return; @@ -1657,58 +1677,6 @@ void CodeCompletionCallbacksImpl::doneParsing() { Lookup.addImportModuleNames(); break; } - case CompletionKind::CallArg: { - ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); - - bool shouldPerformGlobalCompletion = true; - - if (ShouldCompleteCallPatternAfterParen && - !ContextInfo.getPossibleCallees().empty()) { - Lookup.setHaveLParen(true); - for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) { - auto apply = ContextInfo.getAnalyzedExpr(); - if (isa_and_nonnull(apply)) { - Lookup.addSubscriptCallPattern( - typeAndDecl.Type, - dyn_cast_or_null(typeAndDecl.Decl), - typeAndDecl.SemanticContext); - } else { - Lookup.addFunctionCallPattern( - typeAndDecl.Type, - dyn_cast_or_null(typeAndDecl.Decl), - typeAndDecl.SemanticContext); - } - } - Lookup.setHaveLParen(false); - - shouldPerformGlobalCompletion = - !Lookup.FoundFunctionCalls || - (Lookup.FoundFunctionCalls && - Lookup.FoundFunctionsWithoutFirstKeyword); - } else if (!ContextInfo.getPossibleParams().empty()) { - auto params = ContextInfo.getPossibleParams(); - Lookup.addCallArgumentCompletionResults(params); - - shouldPerformGlobalCompletion = !ContextInfo.getPossibleTypes().empty(); - // Fallback to global completion if the position is out of number. It's - // better than suggest nothing. - shouldPerformGlobalCompletion |= llvm::all_of( - params, [](const PossibleParamInfo &P) { return !P.Param; }); - } - - if (shouldPerformGlobalCompletion) { - Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), - ContextInfo.isImplicitSingleExpressionReturn()); - - // Add any keywords that can be used in an argument expr position. - addSuperKeyword(CompletionContext.getResultSink(), CurDeclContext); - addExprKeywords(CompletionContext.getResultSink(), CurDeclContext); - - DoPostfixExprBeginning(); - } - break; - } - case CompletionKind::LabeledTrailingClosure: { ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 301361498684d..9016862e26ca8 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1876,6 +1876,13 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType, return getTypeMatchFailure(locator); } + // If we're solving for code completion and the body contains the code + // completion location, skipping it won't get us to a useful solution so + // just bail. + if (isForCodeCompletion() && containsCodeCompletionLoc(fn.getBody())) { + return getTypeMatchFailure(locator); + } + // Record the first unhandled construct as a fix. if (recordFix( SkipUnhandledConstructInResultBuilder::create( diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index ca318a2eb4f91..35bc46b4dc381 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -2016,11 +2016,18 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { // Don't penalize solutions with unresolved generics. if (TypeVar->getImpl().getGenericParameter()) return false; + // Don't penalize solutions with holes due to missing arguments after the // code completion position. auto argLoc = srcLocator->findLast(); if (argLoc && argLoc->isAfterCodeCompletionLoc()) return false; + + // Don't penailze solutions that have holes for ignored arguments. + if (cs.isArgumentIgnoredForCodeCompletion( + TypeVar->getImpl().getLocator())) { + return false; + } } // Reflect in the score that this type variable couldn't be // resolved and had to be bound to a placeholder "hole" type. diff --git a/lib/Sema/CSClosure.cpp b/lib/Sema/CSClosure.cpp index 13bee8a11518a..b64b9fb8a6b20 100644 --- a/lib/Sema/CSClosure.cpp +++ b/lib/Sema/CSClosure.cpp @@ -895,7 +895,9 @@ class ClosureConstraintGenerator // Single-expression closures are effectively a `return` statement, // so let's give them a special locator as to indicate that. - if (closure->hasSingleExpressionBody()) { + // Return statements might not have a result if we have a closure whose + // implicit returned value is coerced to Void. + if (closure->hasSingleExpressionBody() && returnStmt->hasResult()) { auto *expr = returnStmt->getResult(); assert(expr && "single expression closure without expression?"); diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index b0464b870fcf5..fb95be286feb3 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -747,7 +747,38 @@ namespace { favorCallOverloads(expr, CS, isFavoredDecl); } - + + /// If \p expr is a call and that call contains the code completion token, + /// add the expressions of all arguments after the code completion token to + /// \p ignoredArguemnts. + /// Otherwise, returns an empty vector. + /// Asssumes that we are solving for code completion. + void getArgumentsAfterCodeCompletionToken( + Expr *expr, ConstraintSystem &CS, + SmallVectorImpl &ignoredArguments) { + assert(CS.isForCodeCompletion()); + + /// Don't ignore the rhs argument if the code completion token is the lhs of + /// an operator call. Main use case is the implicit ` ~= $match` + /// call created for pattern matching, in which we need to type-check + /// `$match` to get a contextual type for `` + if (isa(expr)) { + return; + } + + auto args = expr->getArgs(); + auto argInfo = getCompletionArgInfo(expr, CS); + if (!args || !argInfo) { + return; + } + + for (auto argIndex : indices(*args)) { + if (argInfo->isBefore(argIndex)) { + ignoredArguments.push_back(args->get(argIndex).getExpr()); + } + } + } + class ConstraintOptimizer : public ASTWalker { ConstraintSystem &CS; @@ -757,6 +788,10 @@ namespace { CS(cs) {} std::pair walkToExprPre(Expr *expr) override { + if (CS.isArgumentIgnoredForCodeCompletion( + CS.getConstraintLocator(expr))) { + return {false, expr}; + } if (CS.shouldReusePrecheckedType() && !CS.getType(expr)->hasTypeVariable()) { @@ -2053,7 +2088,14 @@ namespace { /*outerAlternatives=*/{}); } - FunctionType *inferClosureType(ClosureExpr *closure) { + /// If \p allowResultBindToHole is \c true, we always allow the closure's + /// result type to bind to a hole, otherwise the result type may only bind + /// to a hole if the closure does not participate in type inference. Setting + /// \p allowResultBindToHole to \c true is useful when ignoring a closure + /// argument in a function call after the code completion token and thus + /// wanting to ignore the closure's type. + FunctionType *inferClosureType(ClosureExpr *closure, + bool allowResultBindToHole = false) { SmallVector closureParams; if (auto *paramList = closure->getParameters()) { @@ -2141,9 +2183,10 @@ namespace { // If this is a multi-statement closure, let's mark result // as potential hole right away. return Type(CS.createTypeVariable( - resultLocator, CS.participatesInInference(closure) - ? 0 - : TVO_CanBindToHole)); + resultLocator, + (!CS.participatesInInference(closure) || allowResultBindToHole) + ? TVO_CanBindToHole + : 0)); }(); // For a non-async function type, add the global actor if present. @@ -3574,6 +3617,26 @@ namespace { } llvm_unreachable("unhandled operation"); } + + /// Assuming that we are solving for code completion, assign \p expr a fresh + /// and unconstrained type variable as its type. + void setTypeForArgumentIgnoredForCompletion(Expr *expr) { + assert(CS.isForCodeCompletion()); + ConstraintSystem &CS = getConstraintSystem(); + + if (auto closure = dyn_cast(expr)) { + FunctionType *closureTy = + inferClosureType(closure, /*allowResultBindToHole=*/true); + CS.setClosureType(closure, closureTy); + CS.setType(closure, closureTy); + } else { + TypeVariableType *exprType = CS.createTypeVariable( + CS.getConstraintLocator(expr), + TVO_CanBindToLValue | TVO_CanBindToInOut | TVO_CanBindToNoEscape | + TVO_CanBindToHole); + CS.setType(expr, exprType); + } + } }; class ConstraintWalker : public ASTWalker { @@ -3583,6 +3646,13 @@ namespace { ConstraintWalker(ConstraintGenerator &CG) : CG(CG) { } std::pair walkToExprPre(Expr *expr) override { + auto &CS = CG.getConstraintSystem(); + + if (CS.isArgumentIgnoredForCodeCompletion( + CS.getConstraintLocator(expr))) { + CG.setTypeForArgumentIgnoredForCompletion(expr); + return {false, expr}; + } if (CG.getConstraintSystem().shouldReusePrecheckedType()) { if (expr->getType()) { @@ -3650,6 +3720,15 @@ namespace { return { false, expr }; } + if (CS.isForCodeCompletion()) { + SmallVector ignoredArgs; + getArgumentsAfterCodeCompletionToken(expr, CS, ignoredArgs); + for (auto ignoredArg : ignoredArgs) { + CS.markArgumentIgnoredForCodeCompletion( + CS.getConstraintLocator(ignoredArg)); + } + } + return { true, expr }; } diff --git a/lib/Sema/CSRanking.cpp b/lib/Sema/CSRanking.cpp index 536ee9fcf3e7b..fcc0886938046 100644 --- a/lib/Sema/CSRanking.cpp +++ b/lib/Sema/CSRanking.cpp @@ -1322,13 +1322,16 @@ SolutionCompareResult ConstraintSystem::compareSolutions( // The systems are not considered equivalent. identical = false; - // A concrete type is better than an archetype. + // Archetypes are worse than concrete types (i.e. non-placeholder and + // non-archetype) // FIXME: Total hack. - if (type1->is() != type2->is()) { - if (type1->is()) - ++score2; - else - ++score1; + if (type1->is() && !type2->is() && + !type2->is()) { + ++score2; + continue; + } else if (type2->is() && !type1->is() && + !type1->is()) { + ++score2; continue; } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 89194a0c58967..c7b1018c6c4df 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -66,6 +66,15 @@ bool MatchCallArgumentListener::relabelArguments(ArrayRef newNames){ return true; } +bool MatchCallArgumentListener::shouldClaimArgDuringRecovery(unsigned argIdx) { + return true; +} + +bool MatchCallArgumentListener::canClaimArgIgnoringNameMismatch( + const AnyFunctionType::Param &arg) { + return false; +} + /// Produce a score (smaller is better) comparing a parameter name and /// potentially-typo'd argument name. /// @@ -254,6 +263,15 @@ static bool anyParameterRequiresArgument( return false; } +static bool isCodeCompletionTypeVar(Type type) { + if (auto *TVT = type->getAs()) { + if (TVT->getImpl().isCodeCompletionToken()) { + return true; + } + } + return false; +} + static bool matchCallArgumentsImpl( SmallVectorImpl &args, ArrayRef params, @@ -340,7 +358,10 @@ static bool matchCallArgumentsImpl( // Go hunting for an unclaimed argument whose name does match. Optional claimedWithSameName; + unsigned firstArgIdx = nextArgIdx; for (unsigned i = nextArgIdx; i != numArgs; ++i) { + auto argLabel = args[i].getLabel(); + bool claimIgnoringNameMismatch = false; if (!args[i].matchParameterLabel(paramLabel)) { // If this is an attempt to claim additional unlabeled arguments @@ -348,9 +369,15 @@ static bool matchCallArgumentsImpl( if (forVariadic) return None; - // Otherwise we can continue trying to find argument which - // matches parameter with or without label. - continue; + if ((i == firstArgIdx || ignoreNameMismatch) && + listener.canClaimArgIgnoringNameMismatch(args[i])) { + // Avoid triggering relabelling fixes about the completion arg. + claimIgnoringNameMismatch = true; + } else { + // Otherwise we can continue trying to find argument which + // matches parameter with or without label. + continue; + } } // Skip claimed arguments. @@ -374,14 +401,14 @@ static bool matchCallArgumentsImpl( // func foo(_ a: Int, _ b: Int = 0, c: Int = 0, _ d: Int) {} // foo(1, c: 2, 3) // -> `3` will be claimed as '_ b:'. // ``` - if (args[i].getLabel().empty()) + if (argLabel.empty() && !claimIgnoringNameMismatch) continue; potentiallyOutOfOrder = true; } // Claim it. - return claim(paramLabel, i); + return claim(paramLabel, i, claimIgnoringNameMismatch); } // If we're not supposed to attempt any fixes, we're done. @@ -585,6 +612,10 @@ static bool matchCallArgumentsImpl( llvm::SmallVector unclaimedNamedArgs; for (auto argIdx : indices(args)) { if (claimedArgs[argIdx]) continue; + + if (!listener.shouldClaimArgDuringRecovery(argIdx)) + continue; + if (!args[argIdx].getLabel().empty()) unclaimedNamedArgs.push_back(argIdx); } @@ -661,6 +692,9 @@ static bool matchCallArgumentsImpl( continue; bindNextParameter(paramIdx, nextArgIdx, true); + + if (!listener.shouldClaimArgDuringRecovery(nextArgIdx)) + continue; } } @@ -674,12 +708,15 @@ static bool matchCallArgumentsImpl( continue; // If parameter has a default value, we don't really - // now if label doesn't match because it's incorrect + // know if label doesn't match because it's incorrect // or argument belongs to some other parameter, so // we just leave this parameter unfulfilled. if (paramInfo.hasDefaultArgument(i)) continue; + if (!listener.shouldClaimArgDuringRecovery(i)) + continue; + // Looks like there was no parameter claimed at the same // position, it could only mean that label is completely // different, because typo correction has been attempted already. @@ -783,8 +820,8 @@ static bool matchCallArgumentsImpl( for (const auto &binding : parameterBindings) { paramToArgMap.push_back(argIdx); // Ignore argument bindings that were synthesized due to missing args. - argIdx += llvm::count_if( - binding, [numArgs](unsigned argIdx) { return argIdx < numArgs; }); + argIdx += llvm::count_if( + binding, [numArgs](unsigned argIdx) { return argIdx < numArgs; }); } } @@ -994,35 +1031,32 @@ constraints::matchCallArguments( }; } -struct CompletionArgInfo { - unsigned completionIdx; - Optional firstTrailingIdx; +bool CompletionArgInfo::allowsMissingArgAt(unsigned argInsertIdx, + AnyFunctionType::Param param) { + // If the argument is before or at the index of the argument containing the + // completion, the user would likely have already written it if they + // intended this overload. + if (completionIdx >= argInsertIdx) { + return false; + } - bool isAllowableMissingArg(unsigned argInsertIdx, - AnyFunctionType::Param param) { - // If the argument is before or at the index of the argument containing the - // completion, the user would likely have already written it if they - // intended this overload. - if (completionIdx >= argInsertIdx) + // If the argument is after the first trailing closure, the user can only + // continue on to write more trailing arguments, so only allow this overload + // if the missing argument is of function type. + if (firstTrailingIdx && argInsertIdx > *firstTrailingIdx) { + if (param.isInOut()) { return false; - - // If the argument is after the first trailing closure, the user can only - // continue on to write more trailing arguments, so only allow this overload - // if the missing argument is of function type. - if (firstTrailingIdx && argInsertIdx > *firstTrailingIdx) { - if (param.isInOut()) - return false; - - Type expectedTy = param.getPlainType()->lookThroughAllOptionalTypes(); - return expectedTy->is() || expectedTy->isAny() || - expectedTy->isTypeVariableOrMember(); } - return true; + + Type expectedTy = param.getPlainType()->lookThroughAllOptionalTypes(); + return expectedTy->is() || expectedTy->isAny() || + expectedTy->isTypeVariableOrMember(); } -}; + return true; +} -static Optional -getCompletionArgInfo(ASTNode anchor, ConstraintSystem &CS) { +Optional +constraints::getCompletionArgInfo(ASTNode anchor, ConstraintSystem &CS) { auto *exprAnchor = getAsExpr(anchor); if (!exprAnchor) return None; @@ -1033,20 +1067,46 @@ getCompletionArgInfo(ASTNode anchor, ConstraintSystem &CS) { for (unsigned i : indices(*args)) { if (CS.containsCodeCompletionLoc(args->getExpr(i))) - return CompletionArgInfo{i, args->getFirstTrailingClosureIndex()}; + return CompletionArgInfo{i, args->getFirstTrailingClosureIndex(), + args->size()}; } return None; } class ArgumentFailureTracker : public MatchCallArgumentListener { +protected: ConstraintSystem &CS; SmallVectorImpl &Arguments; ArrayRef Parameters; ConstraintLocatorBuilder Locator; +private: SmallVector MissingArguments; SmallVector, 4> ExtraArguments; - Optional CompletionArgInfo; + +protected: + /// Synthesizes an argument that is intended to match against a missing + /// argument for the parameter at \p paramIdx. + /// \returns The index of the new argument in \c Arguments. + unsigned synthesizeArgument(unsigned paramIdx, + bool isAfterCodeCompletionLoc) { + const auto ¶m = Parameters[paramIdx]; + + unsigned newArgIdx = Arguments.size(); + auto *argLoc = CS.getConstraintLocator( + Locator, {LocatorPathElt::ApplyArgToParam(newArgIdx, paramIdx, + param.getParameterFlags()), + LocatorPathElt::SynthesizedArgument( + newArgIdx, isAfterCodeCompletionLoc)}); + + auto *argType = CS.createTypeVariable( + argLoc, TVO_CanBindToInOut | TVO_CanBindToLValue | + TVO_CanBindToNoEscape | TVO_CanBindToHole); + + auto synthesizedArg = param.withType(argType); + Arguments.push_back(synthesizedArg); + return newArgIdx; + } public: ArgumentFailureTracker(ConstraintSystem &cs, @@ -1070,36 +1130,9 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { if (!CS.shouldAttemptFixes()) return None; - const auto ¶m = Parameters[paramIdx]; - - unsigned newArgIdx = Arguments.size(); - - bool isAfterCodeCompletionLoc = false; - if (CS.isForCodeCompletion()) { - if (!CompletionArgInfo) - CompletionArgInfo = getCompletionArgInfo(Locator.getAnchor(), CS); - isAfterCodeCompletionLoc = CompletionArgInfo && - CompletionArgInfo->isAllowableMissingArg(argInsertIdx, param); - } - - auto *argLoc = CS.getConstraintLocator( - Locator, {LocatorPathElt::ApplyArgToParam(newArgIdx, paramIdx, - param.getParameterFlags()), - LocatorPathElt::SynthesizedArgument(newArgIdx, isAfterCodeCompletionLoc)}); - - auto *argType = - CS.createTypeVariable(argLoc, TVO_CanBindToInOut | TVO_CanBindToLValue | - TVO_CanBindToNoEscape | TVO_CanBindToHole); - - auto synthesizedArg = param.withType(argType); - Arguments.push_back(synthesizedArg); - - // When solving for code completion, if any argument contains the - // completion location, later arguments shouldn't be considered missing - // (causing the solution to have a worse score) as the user just hasn't - // written them yet. Early exit to avoid recording them in this case. - if (isAfterCodeCompletionLoc) - return newArgIdx; + unsigned newArgIdx = + synthesizeArgument(paramIdx, /*isAfterCodeCompletionLoc=*/false); + auto synthesizedArg = Arguments[newArgIdx]; MissingArguments.push_back(SynthesizedArg{paramIdx, synthesizedArg}); @@ -1213,6 +1246,64 @@ class ArgumentFailureTracker : public MatchCallArgumentListener { } }; +/// Ignores any failures after the code comletion token. +class CompletionArgumentTracker : public ArgumentFailureTracker { + struct CompletionArgInfo ArgInfo; + +public: + CompletionArgumentTracker(ConstraintSystem &cs, + SmallVectorImpl &args, + ArrayRef params, + ConstraintLocatorBuilder locator, + struct CompletionArgInfo ArgInfo) + : ArgumentFailureTracker(cs, args, params, locator), ArgInfo(ArgInfo) {} + + Optional missingArgument(unsigned paramIdx, + unsigned argInsertIdx) override { + // When solving for code completion, if any argument contains the + // completion location, later arguments shouldn't be considered missing + // (causing the solution to have a worse score) as the user just hasn't + // written them yet. Early exit to avoid recording them in this case. + if (ArgInfo.allowsMissingArgAt(argInsertIdx, Parameters[paramIdx])) { + return synthesizeArgument(paramIdx, /*isAfterCodeCompletionLoc=*/true); + } + + return ArgumentFailureTracker::missingArgument(paramIdx, argInsertIdx); + } + + bool extraArgument(unsigned argIdx) override { + if (ArgInfo.isBefore(argIdx)) { + return false; + } + return ArgumentFailureTracker::extraArgument(argIdx); + } + + bool outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx, + ArrayRef bindings) override { + if (ArgInfo.isBefore(argIdx)) { + return false; + } + + return ArgumentFailureTracker::outOfOrderArgument(argIdx, prevArgIdx, + bindings); + } + + bool shouldClaimArgDuringRecovery(unsigned argIdx) override { + return !ArgInfo.isBefore(argIdx); + } + + bool + canClaimArgIgnoringNameMismatch(const AnyFunctionType::Param &arg) override { + if (!isCodeCompletionTypeVar(arg.getPlainType())) { + return false; + } + if (!arg.getLabel().empty()) { + return false; + } + return true; + } +}; + class AllowLabelMismatches : public MatchCallArgumentListener { SmallVector NewLabels; bool HadLabelingIssues = false; @@ -1541,11 +1632,23 @@ static ConstraintSystem::TypeMatchResult matchCallArguments( TrailingClosureMatching::Forward; { - ArgumentFailureTracker listener(cs, argsWithLabels, params, locator); + std::unique_ptr listener; + if (cs.isForCodeCompletion()) { + if (auto completionInfo = getCompletionArgInfo(locator.getAnchor(), cs)) { + listener = std::make_unique( + cs, argsWithLabels, params, locator, *completionInfo); + } + } + if (!listener) { + // We didn't create an argument tracker for code completion. Create a + // normal one. + listener = std::make_unique(cs, argsWithLabels, + params, locator); + } auto callArgumentMatch = constraints::matchCallArguments( argsWithLabels, params, paramInfo, argList->getFirstTrailingClosureIndex(), cs.shouldAttemptFixes(), - listener, trailingClosureMatching); + *listener, trailingClosureMatching); if (!callArgumentMatch) return cs.getTypeMatchFailure(locator); @@ -1572,7 +1675,7 @@ static ConstraintSystem::TypeMatchResult matchCallArguments( // Take the parameter bindings we selected. parameterBindings = std::move(callArgumentMatch->parameterBindings); - auto extraArguments = listener.getExtraneousArguments(); + auto extraArguments = listener->getExtraneousArguments(); if (!extraArguments.empty()) { if (RemoveExtraneousArguments::isMinMaxNameShadowing(cs, locator)) return cs.getTypeMatchFailure(locator); @@ -7719,14 +7822,14 @@ static bool isForKeyPathSubscript(ConstraintSystem &cs, return false; } -static bool isForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs, - ConstraintLocator *locator) { +static bool mayBeForKeyPathSubscriptWithoutLabel(ConstraintSystem &cs, + ConstraintLocator *locator) { if (!locator || !locator->getAnchor()) return false; if (auto *SE = getAsExpr(locator->getAnchor())) { if (auto *unary = SE->getArgs()->getUnlabeledUnaryExpr()) - return isa(unary); + return isa(unary) || isa(unary); } return false; } @@ -7862,8 +7965,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, // subscript without a `keyPath:` label. Add it to the result so that it // can be caught by the missing argument label checking later. if (isForKeyPathSubscript(*this, memberLocator) || - (isForKeyPathSubscriptWithoutLabel(*this, memberLocator) - && includeInaccessibleMembers)) { + (mayBeForKeyPathSubscriptWithoutLabel(*this, memberLocator) && + includeInaccessibleMembers)) { if (baseTy->isAnyObject()) { result.addUnviable( OverloadChoice(baseTy, OverloadChoiceKind::KeyPathApplication), @@ -8011,8 +8114,10 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, if (decl->isRecursiveValidation()) return; - // If the result is invalid, skip it. - if (decl->isInvalid()) { + // If the result is invalid, skip it unless solving for code completion + // For code completion include the result because we can partially match + // against function types that only have one parameter with error type. + if (decl->isInvalid() && !isForCodeCompletion()) { result.markErrorAlreadyDiagnosed(); return; } @@ -10555,7 +10660,17 @@ ConstraintSystem::simplifyKeyPathApplicationConstraint( return matchTypes(resultTy, valueTy, ConstraintKind::Bind, subflags, locator); } - + + if (keyPathTy->isPlaceholder()) { + if (rootTy->hasTypeVariable()) { + recordAnyTypeVarAsPotentialHole(rootTy); + } + if (valueTy->hasTypeVariable()) { + recordAnyTypeVarAsPotentialHole(valueTy); + } + return SolutionKind::Solved; + } + if (auto bgt = keyPathTy->getAs()) { // We have the key path type. Match it to the other ends of the constraint. auto kpRootTy = bgt->getGenericArgs()[0]; @@ -12355,6 +12470,11 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { log << ")\n"; } + if (isArgumentIgnoredForCodeCompletion(fix->getLocator())) { + // The argument was ignored. Don't record any fixes for it. + return false; + } + // Record the fix. // If this is just a warning, it shouldn't affect the solver. Otherwise, diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 30e6315fb3747..20f218405255e 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -341,8 +341,16 @@ ConstraintSystem::getAlternativeLiteralTypes(KnownProtocolKind kind, return scratch; } -bool ConstraintSystem::containsCodeCompletionLoc(Expr *expr) const { - SourceRange range = expr->getSourceRange(); +bool ConstraintSystem::containsCodeCompletionLoc(ASTNode node) const { + SourceRange range = node.getSourceRange(); + if (range.isInvalid()) + return false; + return Context.SourceMgr.rangeContainsCodeCompletionLoc(range); +} + +bool ConstraintSystem::containsCodeCompletionLoc( + const ArgumentList *args) const { + SourceRange range = args->getSourceRange(); if (range.isInvalid()) return false; return Context.SourceMgr.rangeContainsCodeCompletionLoc(range); @@ -1938,6 +1946,38 @@ Type constraints::typeEraseOpenedExistentialReference( }); } +/// For every parameter in \p type that has an error type, replace that +/// parameter's type by a placeholder type, where \p value is the declaration +/// that declared \p type. This is useful for code completion so we can match +/// the types we do know instead of bailing out completely because \p type +/// contains an error type. +static Type replaceParamErrorTypeByPlaceholder(Type type, ValueDecl *value) { + if (!type->is() || !isa(value)) { + return type; + } + auto funcType = type->castTo(); + auto funcDecl = cast(value); + + auto declParams = funcDecl->getParameters(); + auto typeParams = funcType->getParams(); + assert(declParams->size() == typeParams.size()); + SmallVector newParams; + newParams.reserve(declParams->size()); + for (auto i : indices(typeParams)) { + AnyFunctionType::Param param = typeParams[i]; + if (param.getPlainType()->is()) { + auto paramDecl = declParams->get(i); + auto placeholder = + PlaceholderType::get(paramDecl->getASTContext(), paramDecl); + newParams.push_back(param.withType(placeholder)); + } else { + newParams.push_back(param); + } + } + assert(newParams.size() == declParams->size()); + return FunctionType::get(newParams, funcType->getResult()); +} + std::pair ConstraintSystem::getTypeOfMemberReference( Type baseTy, ValueDecl *value, DeclContext *useDC, @@ -2019,6 +2059,10 @@ ConstraintSystem::getTypeOfMemberReference( if (isa(value) || isa(value)) { + if (auto ErrorTy = value->getInterfaceType()->getAs()) { + return {ErrorType::get(ErrorTy->getASTContext()), + ErrorType::get(ErrorTy->getASTContext())}; + } // This is the easy case. funcType = value->getInterfaceType()->castTo(); @@ -2248,6 +2292,12 @@ ConstraintSystem::getTypeOfMemberReference( if (isReferenceOptional && !isa(value)) type = OptionalType::get(type->getRValueType()); + if (isForCodeCompletion() && type->hasError()) { + // In code completion, replace error types by placeholder types so we can + // match the types we know instead of bailing out completely. + type = replaceParamErrorTypeByPlaceholder(type, value); + } + // If we opened up any type variables, record the replacements. recordOpenedTypes(locator, replacements); @@ -3200,7 +3250,13 @@ void ConstraintSystem::resolveOverload(ConstraintLocator *locator, refType = subscriptTy; // Increase the score so that actual subscripts get preference. - increaseScore(SK_KeyPathSubscript); + // ...except if we're solving for code completion and the index expression + // contains the completion location + auto SE = getAsExpr(locator->getAnchor()); + if (!isForCodeCompletion() || + (SE && !containsCodeCompletionLoc(SE->getArgs()))) { + increaseScore(SK_KeyPathSubscript); + } break; } } diff --git a/lib/Sema/TypeCheckCodeCompletion.cpp b/lib/Sema/TypeCheckCodeCompletion.cpp index 8c44b905c3c25..3b11b12885968 100644 --- a/lib/Sema/TypeCheckCodeCompletion.cpp +++ b/lib/Sema/TypeCheckCodeCompletion.cpp @@ -189,13 +189,19 @@ class SanitizeExpr : public ASTWalker { continue; } - // Restore '@autoclosure'd value. if (auto ACE = dyn_cast(expr)) { + // Restore '@autoclosure'd value. // This is only valid if the closure doesn't have parameters. if (ACE->getParameters()->size() == 0) { expr = ACE->getSingleExpressionBody(); continue; } + // Restore autoclosure'd function reference. + if (auto *unwrapped = ACE->getUnwrappedCurryThunkExpr()) { + expr = unwrapped; + continue; + } + llvm_unreachable("other AutoClosureExpr must be handled specially"); } @@ -548,8 +554,7 @@ void TypeChecker::filterSolutionsForCodeCompletion( })->getFixedScore(); llvm::erase_if(solutions, [&](const Solution &S) { - return S.getFixedScore().Data[SK_Fix] != 0 && - S.getFixedScore() > minScore; + return S.getFixedScore().Data[SK_Fix] > minScore.Data[SK_Fix]; }); } diff --git a/test/IDE/complete_ambiguous.swift b/test/IDE/complete_ambiguous.swift index 628e333dcf1c4..d4e840bbf8983 100644 --- a/test/IDE/complete_ambiguous.swift +++ b/test/IDE/complete_ambiguous.swift @@ -383,6 +383,10 @@ _ = testing([Point(4, 89)]) { arg in struct Thing { init(_ block: (Point) -> Void) {} + + enum ThingEnum { case first, second } + func doStuff(_ x: ThingEnum, _ y: Int) -> Thing { return self } + func takesRef(_ ref: () -> ()) -> Thing { return self } } @resultBuilder struct ThingBuilder { @@ -429,6 +433,20 @@ CreateThings { Thing. // ErrorExpr } +struct TestFuncBodyBuilder { + func someFunc() {} + + @ThingBuilder func foo() -> [Thing] { + Thing() + .doStuff(.#^FUNCBUILDER_FUNCBODY^#, 3) + .takesRef(someFunc) + } +} +// FUNCBUILDER_FUNCBODY: Begin completions, 3 items +// FUNCBUILDER_FUNCBODY-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: first[#Thing.ThingEnum#]; +// FUNCBUILDER_FUNCBODY-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: second[#Thing.ThingEnum#]; +// FUNCBUILDER_FUNCBODY-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): Thing.ThingEnum#})[#(into: inout Hasher) -> Void#]; +// FUNCBUILDER_FUNCBODY: End completions func takesClosureOfPoint(_: (Point)->()) {} func overloadedWithDefaulted(_: ()->()) {} @@ -473,3 +491,89 @@ func testBestSolutionGeneric() { // BEST_SOLUTION_FILTER_GEN-DAG: Keyword[self]/CurrNominal: self[#Test1#]; name=self // BEST_SOLUTION_FILTER_GEN: End completions +func testAmbiguousArgs() { + struct A { + func someFunc(a: Int, b: Int) -> A { return self } + func variadic(y: Int, x: Int...) + } + + struct B { + func someFunc(a: Int, c: String = "", d: String = "") {} + } + + func overloaded() -> A { return A() } + func overloaded() -> B { return B() } + + let localInt = 10 + let localString = "Hello" + + overloaded().someFunc(a: 2, #^ARG_NO_LABEL^#) + + // ARG_NO_LABEL: Begin completions, 3 items + // ARG_NO_LABEL-DAG: Pattern/Local/Flair[ArgLabels]: {#b: Int#}[#Int#]; name=b: + // ARG_NO_LABEL-DAG: Pattern/Local/Flair[ArgLabels]: {#c: String#}[#String#]; name=c: + // ARG_NO_LABEL-DAG: Pattern/Local/Flair[ArgLabels]: {#d: String#}[#String#]; name=d: + // ARG_NO_LABEL: End completions + + overloaded().someFunc(a: 2, b: #^ARG_LABEL^#) + + // ARG_LABEL: Begin completions + // ARG_LABEL-DAG: Decl[LocalVar]/Local: localString[#String#]; name=localString + // ARG_LABEL-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: localInt[#Int#]; name=localInt + // ARG_LABEL: End completions + + overloaded().someFunc(a: 2, c: "Foo", #^ARG_NO_LABEL2^#) + + // ARG_NO_LABEL2: Begin completions, 1 item + // ARG_NO_LABEL2: Pattern/Local/Flair[ArgLabels]: {#d: String#}[#String#]; name=d: + // ARG_NO_LABEL2: End completions + + overloaded().someFunc(a: 2, wrongLabel: "Foo", #^ARG_NO_LABEL_PREV_WRONG^#) + + // ARG_NO_LABEL_PREV_WRONG: Begin completions, 3 items + // ARG_NO_LABEL_PREV_WRONG-DAG: Pattern/Local/Flair[ArgLabels]: {#b: Int#}[#Int#]; name=b: + // ARG_NO_LABEL_PREV_WRONG-DAG: Pattern/Local/Flair[ArgLabels]: {#c: String#}[#String#]; name=c: + // ARG_NO_LABEL_PREV_WRONG-DAG: Pattern/Local/Flair[ArgLabels]: {#d: String#}[#String#]; name=d: + // ARG_NO_LABEL_PREV_WRONG: End completions + + overloaded().someFunc(a: 2, d: "Foo", #^ARG_NO_LABEL_OUT_OF_ORDER^#) + // ARG_NO_LABEL_OUT_OF_ORDER: Begin completions + // ARG_NO_LABEL_OUT_OF_ORDER-NOT: name=d: String + // ARG_NO_LABEL_OUT_OF_ORDER: Pattern/Local/Flair[ArgLabels]: {#c: String#}[#String#]; name=c: + // ARG_NO_LABEL_OUT_OF_ORDER-NOT: name=d: String + // ARG_NO_LABEL_OUT_OF_ORDER: End completions + + func noArgs() {} + noArgs(12, #^ARG_EXTRANEOUS^#) + // ARG_EXTRANEOUS: Begin completions + // ARG_EXTRANEOUS-DAG: localInt + // ARG_EXTRANEOUS-DAG: localString + // ARG_EXTRANEOUS: End completions + + overloaded().someFunc(a: 2, #^LATER_ARGS^#, d: "foo") + // LATER_ARGS: Begin completions, 2 items + + // LATER_ARGS-DAG: Pattern/Local/Flair[ArgLabels]: {#b: Int#}[#Int#]; name=b: + // LATER_ARGS-DAG: Pattern/Local/Flair[ArgLabels]: {#c: String#}[#String#]; name=c: + // LATER_ARGS: End completions + + overloaded().someFunc(a: 2, #^LATER_ARGS_WRONG^#, k: 4.5) + // LATER_ARGS_WRONG: Begin completions, 3 items + // LATER_ARGS_WRONG-DAG: Pattern/Local/Flair[ArgLabels]: {#b: Int#}[#Int#]; name=b: + // LATER_ARGS_WRONG-DAG: Pattern/Local/Flair[ArgLabels]: {#c: String#}[#String#]; name=c: + // LATER_ARGS_WRONG-DAG: Pattern/Local/Flair[ArgLabels]: {#d: String#}[#String#]; name=d: + // LATER_ARGS_WRONG-DAG: End completions + + + overloaded().variadic(y: 2, #^INITIAL_VARARG^#, 4) + // INITIAL_VARARG: Begin completions, 1 item + // INITIAL_VARARG: Pattern/Local/Flair[ArgLabels]: {#x: Int...#}[#Int#]; name=x: + // INITIAL VARARG: End completions + + overloaded().variadic(y: 2, x: 2, #^NONINITIAL_VARARG^#) + // NONINITIAL_VARARG: Begin completions + // NONINITIAL_VARARG-NOT: name=x: + // NONINITIAL_VARARG: Decl[LocalVar]/Local/TypeRelation[Identical]: localInt[#Int#]; name=localInt + // NONINITIAL_VARARG-NOT: name=x: + // NONINITIAL_VARARG: End completions +} diff --git a/test/IDE/complete_call_arg.swift b/test/IDE/complete_call_arg.swift index e89d07500ae36..5bbceb99a29a2 100644 --- a/test/IDE/complete_call_arg.swift +++ b/test/IDE/complete_call_arg.swift @@ -180,10 +180,10 @@ class C3 { var C1I = C1() var C2I = C2() func f1() { - foo2(C1I, #^OVERLOAD1?xfail=FIXME^#) + foo2(C1I, #^OVERLOAD1^#) } func f2() { - foo2(C2I, #^OVERLOAD2?xfail=FIXME^#) + foo2(C2I, #^OVERLOAD2^#) } func f3() { foo2(C1I, b1: #^OVERLOAD3^#) @@ -207,11 +207,11 @@ class C3 { } // OVERLOAD1: Begin completions, 1 items -// OVERLOAD1-NEXT: Keyword/ExprSpecific: b1: [#Argument name#]; name=b1: +// OVERLOAD1-NEXT: Pattern/Local/Flair[ArgLabels]: {#b1: C2#}[#C2#]; name=b1: // OVERLOAD1-NEXT: End completions // OVERLOAD2: Begin completions, 1 items -// OVERLOAD2-NEXT: Keyword/ExprSpecific: b2: [#Argument name#]; name=b2: +// OVERLOAD2-NEXT: Pattern/Local/Flair[ArgLabels]: {#b2: C1#}[#C1#]; name=b2: // OVERLOAD2-NEXT: End completions // OVERLOAD3: Begin completions @@ -221,9 +221,6 @@ class C3 { // OVERLOAD3-DAG: Decl[Class]/CurrModule/TypeRelation[Identical]: C2[#C2#] // OVERLOAD3: End completions -// FIXME: This should be a negative test case -// NEGATIVE_OVERLOAD_3-NOT: Decl[Class]{{.*}} C1 - // OVERLOAD4: Begin completions // OVERLOAD4-DAG: Decl[InstanceVar]/CurrNominal/TypeRelation[Identical]: C1I[#C1#]; name=C1I // OVERLOAD4-DAG: Decl[InstanceVar]/CurrNominal: C2I[#C2#]; name=C2I @@ -231,9 +228,6 @@ class C3 { // OVERLOAD4-DAG: Decl[Class]/CurrModule/TypeRelation[Identical]: C1[#C1#] // OVERLOAD4: End completions -// FIXME: This should be a negative test case -// NEGATIVE_OVERLOAD4-NOT: Decl[Class]{{.*}} C2 - // OVERLOAD5: Begin completions // OVERLOAD5-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#(a): C1#}, {#b1: C2#}[')'][#Void#]; name=:b1: // OVERLOAD5-DAG: Decl[FreeFunction]/CurrModule/Flair[ArgLabels]: ['(']{#(a): C2#}, {#b2: C1#}[')'][#Void#]; name=:b2: @@ -269,7 +263,7 @@ extension C3 { // HASERROR2: End completions // HASERROR3: Begin completions -// HASERROR3-DAG: Pattern/Local/Flair[ArgLabels]: {#b1: <>#}[#<>#]; +// HASERROR3-DAG: Pattern/Local/Flair[ArgLabels]: {#b1: _#}[#_#]; // HASERROR3: End completions // HASERROR4: Begin completions @@ -992,8 +986,10 @@ struct Rdar77867723 { } func test2 { self.fn(eee: .up, #^OVERLOAD_LABEL2^#) -// OVERLOAD_LABEL2: Begin completions, 2 items +// OVERLOAD_LABEL2: Begin completions, 4 items +// OVERLOAD_LABEL2-DAG: Pattern/Local/Flair[ArgLabels]: {#aaa: Horizontal#}[#Horizontal#]; // OVERLOAD_LABEL2-DAG: Pattern/Local/Flair[ArgLabels]: {#bbb: Vertical#}[#Vertical#]; +// OVERLOAD_LABEL2-DAG: Pattern/Local/Flair[ArgLabels]: {#ccc: Vertical#}[#Vertical#]; // OVERLOAD_LABEL2-DAG: Pattern/Local/Flair[ArgLabels]: {#ddd: Horizontal#}[#Horizontal#]; // OVERLOAD_LABEL2: End completions } @@ -1008,10 +1004,370 @@ extension SR14737 where T == Int { func test_SR14737() { invalidCallee { SR14737(arg1: true, #^GENERIC_INIT_IN_INVALID^#) -// FIXME: 'onlyInt' shouldn't be offered because 'arg1' is Bool. -// GENERIC_INIT_IN_INVALID: Begin completions, 2 items +// GENERIC_INIT_IN_INVALID: Begin completions, 1 item // GENERIC_INIT_IN_INVALID-DAG: Pattern/Local/Flair[ArgLabels]: {#arg2: Bool#}[#Bool#]; -// GENERIC_INIT_IN_INVALID-DAG: Pattern/Local/Flair[ArgLabels]: {#onlyInt: Bool#}[#Bool#]; // GENERIC_INIT_IN_INVALID: End completions } } + +struct CondConfType { + init(_: U) +} +extension CondConfType: Equatable where U == Int {} + +func testArgsAfterCompletion() { + enum A { case a } + enum B { case b } + + let localA = A.a + let localB = B.b + + func overloaded(x: Int, _ first: A, _ second: B) {} + func overloaded(x: Int, _ first: B, _ second: A) {} + struct SubOverloaded { + subscript(x x: Int, first: A, second: B) -> Int { return 1 } + subscript(x x: Int, first: B, second: A) -> Int { return 1} + } + + overloaded(x: 1, .#^VALID_UNRESOLVED?check=VALID_UNRESOLVED_NOCOMMA^#, localB) + SubOverloaded()[x: 1, .#^VALID_UNRESOLVED_SUB?check=VALID_UNRESOLVED_NOCOMMA^#, localB] + + // VALID_UNRESOLVED: Begin completions, 2 items + // VALID_UNRESOLVED-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: a[#A#]; name=a + // VALID_UNRESOLVED-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): A#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // VALID_UNRESOLVED: End completions + + overloaded(x: 1, .#^VALID_UNRESOLVED_NOCOMMA^# localB) + SubOverloaded()[x: 1, .#^VALID_UNRESOLVED_NOCOMA_SUB?check=VALID_UNRESOLVED_NOCOMMA^# localB] + + // VALID_UNRESOLVED_NOCOMMA: Begin completions, 4 items + // VALID_UNRESOLVED_NOCOMMA-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: a[#A#]; name=a + // VALID_UNRESOLVED_NOCOMMA-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: b[#B#]; name=b + // VALID_UNRESOLVED_NOCOMMA-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): A#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // VALID_UNRESOLVED_NOCOMMA-DAG: Decl[InstanceMethod]/CurrNominal/TypeRelation[Invalid]: hash({#(self): B#})[#(into: inout Hasher) -> Void#]; name=hash(:) + // VALID_UNRESOLVED_NOCOMMA: End completions + + overloaded(x: 1, .#^INVALID_UNRESOLVED?check=VALID_UNRESOLVED_NOCOMMA^#, "wrongType") + SubOverloaded()[x: 1, .#^INVALID_UNRESOLVED_SUB?check=VALID_UNRESOLVED_NOCOMMA^#, "wrongType"] + + overloaded(x: 1, #^VALID_GLOBAL^#, localB) + SubOverloaded()[x: 1, #^VALID_GLOBAL_SUB?check=VALID_GLOBAL^#, localB] + // VALID_GLOBAL: Begin completions + // VALID_GLOBAL-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: localA[#A#]; name=localA + // VALID_GLOBAL-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: localB[#B#]; name=localB + // VALID_GLOBAL: End completions + + func takesIntGivesB(_ x: Int) -> B { return B.b } + overloaded(x: 1, .#^VALID_NESTED?check=VALID_UNRESOLVED_NOCOMMA^#, takesIntGivesB(1)) + overloaded(x: 1, .#^INVALID_NESTED?check=VALID_UNRESOLVED_NOCOMMA^#, takesIntGivesB("string")) + + func overloadedLabel(x: Int, firstA: A, second: B) {} + func overloadedLabel(x: Int, firstB: B, second: A) {} + struct SubOverloadedLabel { + subscript(x x: Int, firstA firstA: A, second second: B) -> Int { return 1 } + subscript(x x: Int, firstB firstB: B, second second: A) -> Int { return 1 } + } + + overloadedLabel(x: 1, #^VALID_LABEL^#, second: localB) + SubOverloadedLabel()[x: 1, #^VALID_LABEL_SUB?check=VALID_LABEL^#, second: localB] + // VALID_LABEL: Begin completions, 2 items + // VALID_LABEL: Pattern/Local/Flair[ArgLabels]: {#firstA: A#}[#A#]; name=firstA: + // VALID_LABEL: Pattern/Local/Flair[ArgLabels]: {#firstB: B#}[#B#]; name=firstB: + // VALID_LABEL: End completions + + overloadedLabel(x: 1, #^VALID_LABEL_NOCOMMA^# second: localB) + SubOverloadedLabel()[x: 1, #^VALID_LABEL_NOCOMMA_SUB?check=VALID_LABEL_NOCOMMA^# second: localB] + + // VALID_LABEL_NOCOMMA: Begin completions, 2 items + // VALID_LABEL_NOCOMMA-DAG: Pattern/Local/Flair[ArgLabels]: {#firstA: A#}[#A#]; name=firstA: + // VALID_LABEL_NOCOMMA-DAG: Pattern/Local/Flair[ArgLabels]: {#firstB: B#}[#B#]; name=firstB: + // VALID_LABEL_NOCOMMA: End completions + + // The parser eats the existing localB arg, so we still suggest both labels here. + overloadedLabel(x: 1, #^VALID_LABEL_NOCOMMA_NOLABEL?check=VALID_LABEL_NOCOMMA^# localB) + SubOverloadedLabel()[x: 1, #^VALID_LABEL_NOCOMMA_NOLABEL_SUB?check=VALID_LABEL_NOCOMMA^# localB] + + overloadedLabel(x: 1, #^INVALID_LABEL^#, wrongLabelRightType: localB) + SubOverloadedLabel()[x: 1, #^INVALID_LABEL_SUB?check=INVALID_LABEL^#, wrongLabelRightType: localB] + // INVALID_LABEL: Begin completions, 2 items + // INVALID_LABEL-DAG: Pattern/Local/Flair[ArgLabels]: {#firstA: A#}[#A#]; name=firstA: + // INVALID_LABEL-DAG: Pattern/Local/Flair[ArgLabels]: {#firstB: B#}[#B#]; name=firstB: + // INVALID_LABEL: End completions + + overloadedLabel(x: 1, #^INVALID_TYPE?check=INVALID_LABEL^#, second: 34) + SubOverloadedLabel()[x: 1, #^INVALID_TYPE_SUB?check=INVALID_LABEL^#, second: 34] + + overloadedLabel(x: 1, #^INVALID_LABEL_TYPE?check=INVALID_LABEL^#, wrongLabelWrongType: 2) + SubOverloadedLabel()[x: 1, #^INVALID_LABEL_TYPE_SUB?check=INVALID_LABEL^#, wrongLabelWrongType: 2] + + func overloadedArity(x: Int, firstA: A, second: B, third: Double) {} + func overloadedArity(x: Int, firstB: B, second: A) {} + struct SubOverloadedArity { + subscript(x x: Int, firstA firstA: A, second second: B, third third: Double) -> Int { return 1} + subscript(x x: Int, firstB firstB: B, second second: A) -> Int { return 1} + } + + overloadedArity(x: 1, #^VALID_ARITY^#, second: localB, third: 4.5) + SubOverloadedArity()[x: 1, #^VALID_ARITY_SUB?check=VALID_ARITY^#, second: localB, third: 4.5] + // VALID_ARITY: Begin completions, 2 items + // VALID_ARITY: Pattern/Local/Flair[ArgLabels]: {#firstA: A#}[#A#]; name=firstA: + // VALID_ARITY: Pattern/Local/Flair[ArgLabels]: {#firstB: B#}[#B#]; name=firstB: + // VALID_ARITY: End completions + + overloadedArity(x: 1, #^VALID_ARITY_NOCOMMA?check=VALID_ARITY^# second: localB, third: 4.5) + SubOverloadedArity()[x: 1, #^VALID_ARITY_NOCOMMA_SUB?check=VALID_ARITY^# second: localB, third: 4.5] + + overloadedArity(x: 1, #^INVALID_ARITY^#, wrong: localB) + SubOverloadedArity()[x: 1, #^INVALID_ARITY_SUB?check=INVALID_ARITY^#, wrong: localB] + // INVALID_ARITY: Begin completions, 2 items + // INVALID_ARITY-DAG: Pattern/Local/Flair[ArgLabels]: {#firstA: A#}[#A#]; name=firstA: + // INVALID_ARITY-DAG: Pattern/Local/Flair[ArgLabels]: {#firstB: B#}[#B#]; name=firstB: + // INVALID_ARITY: End completions + + // type mismatch in 'second' vs extra arg 'third'. + overloadedArity(x: 1, #^INVALID_ARITY_TYPE?check=INVALID_ARITY^#, second: localA, third: 2.5) + SubOverloadedArity()[x: 1, #^INVALID_ARITY_TYPE_SUB?check=INVALID_ARITY^#, second: localA, third: 2.5] + overloadedArity(x: 1, #^INVALID_ARITY_TYPE_2?check=INVALID_ARITY^#, second: localA, third: "wrong") + SubOverloadedArity()[x: 1, #^INVALID_ARITY_TYPE_2_SUB?check=INVALID_ARITY^#, second: localA, third: "wrong"] + + + func overloadedDefaulted(x: Int, p: A) {} + func overloadedDefaulted(x: Int, y: A = A.a, z: A = A.a) {} + struct SubOverloadedDefaulted { + subscript(x x: Int, p p: A) -> Int { return 1 } + subscript(x x: Int, y y: A = A.a, z z: A = A.a) -> Int { return 1 } + } + + overloadedDefaulted(x: 1, #^VALID_DEFAULTED^#) + SubOverloadedDefaulted()[x: 1, #^VALID_DEFAULTED_SUB?check=VALID_DEFAULTED^#] + // VALID_DEFAULTED: Begin completions, 3 items + // VALID_DEFAULTED-DAG: Pattern/Local/Flair[ArgLabels]: {#p: A#}[#A#]; name=p: + // VALID_DEFAULTED-DAG: Pattern/Local/Flair[ArgLabels]: {#y: A#}[#A#]; name=y: + // VALID_DEFAULTED-DAG: Pattern/Local/Flair[ArgLabels]: {#z: A#}[#A#]; name=z: + // VALID_DEFAULTED: End completions + + overloadedDefaulted(x: 1, #^VALID_DEFAULTED_AFTER^#, z: localA) + SubOverloadedDefaulted()[x: 1, #^VALID_DEFAULTED_AFTER_SUB?check=VALID_DEFAULTED_AFTER^#, z: localA] + // VALID_DEFAULTED_AFTER: Begin completions, 2 items + // VALID_DEFAULTED_AFTER-DAG: Pattern/Local/Flair[ArgLabels]: {#p: A#}[#A#]; name=p: + // VALID_DEFAULTED_AFTER-DAG: Pattern/Local/Flair[ArgLabels]: {#y: A#}[#A#]; name=y: + // VALID_DEFAULTED_AFTER: End completions + + overloadedDefaulted(x: 1, #^VALID_DEFAULTED_AFTER_NOCOMMA?check=VALID_DEFAULTED^# z: localA) + overloadedDefaulted(x: 1, #^INVALID_DEFAULTED?check=VALID_DEFAULTED^#, w: "hello") + overloadedDefaulted(x: 1, #^INVALID_DEFAULTED_TYPO?check=VALID_DEFAULTED^#, zz: localA) + overloadedDefaulted(x: 1, #^INVALID_DEFAULTED_TYPO_TYPE?check=VALID_DEFAULTED^#, zz: "hello") + SubOverloadedDefaulted()[x: 1, #^VALID_DEFAULTED_AFTER_NOCOMMA_SUB?check=VALID_DEFAULTED^# z: localA] + SubOverloadedDefaulted()[x: 1, #^INVALID_DEFAULTED_SUB?check=VALID_DEFAULTED^#, w: "hello"] + SubOverloadedDefaulted()[x: 1, #^INVALID_DEFAULTED_TYPO_SUB?check=VALID_DEFAULTED^#, zz: localA] + SubOverloadedDefaulted()[x: 1, #^INVALID_DEFAULTED_TYPO_TYPE_SUB?check=VALID_DEFAULTED^#, zz: "hello"] + + overloadedDefaulted(x: 1, #^INVALID_DEFAULTED_TYPE^#, z: localB) + SubOverloadedDefaulted()[x: 1, #^INVALID_DEFAULTED_TYPE_SUB?check=INVALID_DEFAULTED_TYPE^#, z: localB] + // INVALID_DEFAULTED_TYPE: Begin completions, 2 items + // INVALID_DEFAULTED_TYPE-DAG: Pattern/Local/Flair[ArgLabels]: {#p: A#}[#A#]; name=p: + // INVALID_DEFAULTED_TYPE-DAG: Pattern/Local/Flair[ArgLabels]: {#y: A#}[#A#]; name=y: + // INVALID_DEFAULTED_TYPE: End completions + + func overloadedGeneric(x: Int, y: String, z: T, zz: T) {} + func overloadedGeneric(x: Int, p: String, q: Int) {} + struct SubOverloadedGeneric { + subscript(x x: Int, y y: String, z z: T, zz zz: T) -> Int { return 1} + subscript(x x: Int, p p: String, q q: Int) -> Int { return 1 } + } + + struct MissingConformance {} + overloadedGeneric(x: 1, #^INVALID_MISSINGCONFORMANCE^#, z: MissingConformance(), zz: MissingConformance()) + SubOverloadedGeneric()[x: 1, #^INVALID_MISSINGCONFORMANCE_SUB?check=INVALID_MISSINGCONFORMANCE^#, z: MissingConformance(), zz: MissingConformance()] + // INVALID_MISSINGCONFORMANCE: Begin completions, 2 items + // INVALID_MISSINGCONFORMANCE-DAG: Pattern/Local/Flair[ArgLabels]: {#p: String#}[#String#]; name=p: + // INVALID_MISSINGCONFORMANCE-DAG: Pattern/Local/Flair[ArgLabels]: {#y: String#}[#String#]; name=y: + // INVALID_MISSINGCONFORMANCE: End completions + + overloadedGeneric(x: 1, #^INVALID_MISSINGCONFORMANCE_NOCOMMA?check=INVALID_MISSINGCONFORMANCE^# z: MisingConformance(), zz: MissingConformance()) + overloadedGeneric(x: 1, #^INVALID_MISSINGCONFORMANCE_INDIRECT?check=INVALID_MISSINGCONFORMANCE^#, z: [MissingConformance()], zz: [MissingConformance()]) + overloadedGeneric(x: 1, #^INVALID_MISSINGCONFORMANCE_CONSTRAINT?check=INVALID_MISSINGCONFORMANCE_CONSTAINT^#, z: [CondConfType("foo")], zz: [CondConfType("bar")]) + SubOverloadedGeneric()[x: 1, #^INVALID_MISSINGCONFORMANCE_NOCOMMA_SUB?check=INVALID_MISSINGCONFORMANCE^# z: MisingConformance(), zz: MissingConformance()] + SubOverloadedGeneric()[x: 1, #^INVALID_MISSINGCONFORMANCE_INDIRECT_SUB?check=INVALID_MISSINGCONFORMANCE^#, z: [MissingConformance()], zz: [MissingConformance()]] + SubOverloadedGeneric()[x: 1, #^INVALID_MISSINGCONFORMANCE_CONSTRAINT_SUB?check=INVALID_MISSINGCONFORMANCE_CONSTAINT^#, z: [CondConfType("foo")], zz: [CondConfType("bar")]] + + // INVALID_MISSINGCONFORMANCE_CONSTAINT: Begin completions, 2 items + // INVALID_MISSINGCONFORMANCE_CONSTAINT-DAG: Pattern/Local/Flair[ArgLabels]: {#y: String#}[#String#]; name=y: + // INVALID_MISSINGCONFORMANCE_CONSTAINT-DAG: Pattern/Local/Flair[ArgLabels]: {#p: String#}[#String#]; name=p: + // INVALID_MISSINGCONFORMANCE_CONSTAINT: End completions +} + +func testFuncTyVars(param: (Int, String, Double) -> ()) { + var local = { (a: Int, b: String, c: Double) in } + + let someInt = 2 + let someString = "hello" + let someDouble = 3.5 + + param(2, #^PARAM_ARG2?check=FUNCTY_STRING^#) + local(2, #^LOCAL_ARG2?check=FUNCTY_STRING^#) + param(2, "hello", #^PARAM_ARG3?check=FUNCTY_DOUBLE^#) + local(2, "hello", #^LOCAL_ARG3?check=FUNCTY_DOUBLE^#) + + // FUNCTY_STRING: Begin completions + // FUNCTY_STRING-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: someString[#String#]; + // FUNCTY_STRING-DAG: Decl[LocalVar]/Local: someDouble[#Double#]; + // FUNCTY_STRING-DAG: Decl[LocalVar]/Local: someInt[#Int#]; + // FUNCTY_STRING: End completions + + // FUNCTY_DOUBLE: Begin completions + // FUNCTY_DOUBLE-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: someDouble[#Double#]; + // FUNCTY_DOUBLE-DAG: Decl[LocalVar]/Local: someString[#String#]; + // FUNCTY_DOUBLE-DAG: Decl[LocalVar]/Local: someInt[#Int#]; + // FUNCTY_DOUBLE: End completions +} + + +private extension Sequence { + func SubstitutableBaseTyOfSubscript(by keyPath: KeyPath) -> [Element] { + return sorted { a, b in a[#^GENERICBASE_SUB^#] } + // GENERICBASE_SUB: Begin completions, 1 item + // GENERICBASE_SUB: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; + // GENERICBASE_SUB: End completions + } +} + + +func testLValueBaseTyOfSubscript() { + var cache: [String: Codable] = [:] + if let cached = cache[#^LVALUEBASETY^# + + // LVALUEBASETY: Begin completions + // LVALUEBASETY-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]/IsSystem: ['[']{#(position): Dictionary.Index#}[']'][#(key: String, value: Codable)#]; + // LVALUEBASETY-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]/IsSystem: ['[']{#(key): String#}[']'][#@lvalue Codable?#]; + // LVALUEBASETY: End completions +} + +func testSkippedCallArgInInvalidResultBuilderBody() { + protocol MyView { + associatedtype Body + var body: Body { get } + } + struct MyEmptyView: MyView { + var body: Never + } + + @resultBuilder public struct MyViewBuilder { + public static func buildBlock() -> MyEmptyView { fatalError() } + public static func buildBlock(_ content: Content) -> Content where Content : MyView { fatalError() } + } + + struct MyImage : MyView { + var body: Never + public init(systemName: String, otherArg: Int) {} + } + + struct Other { + public init(action: Int, @MyViewBuilder label: () -> Label) {} + public init(_ titleKey: Int, action: Int) {} + } + + func foo() -> Bool { + Other(action: 2) { + MyImage(systemName: "", #^INVALID_RESULTBUILDER_ARG^# + struct Invalid + } + + // INVALID_RESULTBUILDER_ARG: Begin completions, 1 item + // INVALID_RESULTBUILDER_ARG: Pattern/Local/Flair[ArgLabels]: {#otherArg: Int#}[#Int#]; + // INVALID_RESULTBUILDER_ARG: End completions +} + +func testFunctionConversionAfterCodecompletionPos() { + func searchSection(_ category: Int, _ items: [String]) -> String {} + + struct ForEach where Data : RandomAccessCollection { + init(_ data: Data, content: (Data.Element) -> String) {} + init(_ data: Data, id: Int, content: (Data.Element) -> String) {} + } + + var searchCategories: [(Int, [String])] + ForEach(searchCategories, #^FUNC_CONVERSION_AFTER_COMPLETION_POS^#id: 0, content: searchSection) +// FUNC_CONVERSION_AFTER_COMPLETION_POS: Begin completions, 2 items +// FUNC_CONVERSION_AFTER_COMPLETION_POS-DAG: Pattern/Local/Flair[ArgLabels]: {#content: ((Int, [String])) -> String##((Int, [String])) -> String#}[#((Int, [String])) -> String#]; +// FUNC_CONVERSION_AFTER_COMPLETION_POS-DAG: Pattern/Local/Flair[ArgLabels]: {#id: Int#}[#Int#]; +// FUNC_CONVERSION_AFTER_COMPLETION_POS: End completions +} + +func testPlaceholderNoBetterThanArchetype() { + // The following test case used to pose a problem because for the `init(header:footer:content)` overload, received receive an opaque type (i.e. an archetype) for `Content` whereas for the `init(header:content:)` overload we are unable to infer it and thus assign a placeholder type to `Content`. Placeholder types were mistakingly ranked higher than archetypes, which made us prefer the `init(header:content:)` overload. + protocol View {} + + struct Section { + init(header: TectionHeaderView, footer: String, content: () -> Content) {} + init(header: TectionHeaderView, content: () -> Content) {} + } + + struct TectionHeaderView { + init(text: String) {} + } + + struct Text: View { + init(_ content: String) {} + func sag(_ tag: String) -> some View { return self } + } + + Section(header: TectionHeaderView(text: "abc"), #^PLACEHOLDER_NO_BETTER_THAN_ARCHETYPE_1^#) { + Text("abc").sag("abc") + } +// PLACEHOLDER_NO_BETTER_THAN_ARCHETYPE_1: Begin completions, 2 items +// PLACEHOLDER_NO_BETTER_THAN_ARCHETYPE_1-DAG: Pattern/Local/Flair[ArgLabels]: {#footer: String#}[#String#]; +// PLACEHOLDER_NO_BETTER_THAN_ARCHETYPE_1-DAG: Pattern/Local/Flair[ArgLabels]: {#content: () -> _##() -> _#}[#() -> _#]; +// PLACEHOLDER_NO_BETTER_THAN_ARCHETYPE_1: End completions + + Section(header: TectionHeaderView(text: "abc"), #^PLACEHOLDER_NO_BETTER_THAN_ARCHETYPE_2?check=PLACEHOLDER_NO_BETTER_THAN_ARCHETYPE_1^#) + +} + +func testConversionBetterThanIgnoreArgs() { + enum Letters { + case a, b, c + } + + enum Numbers { + case one, two, three + } + + func consumeLetterOrNumber(_ letter: Letters) {} + func consumeLetterOrNumber(_ number: Numbers, _ someOtherArg: Int?) {} + + consumeLetterOrNumber(.#^CONVERSION_NO_BETTER_THAN_IGNORE_ARGS^#one, 32) +} + +// CONVERSION_NO_BETTER_THAN_IGNORE_ARGS: Begin completions +// CONVERSION_NO_BETTER_THAN_IGNORE_ARGS-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: one[#Numbers#]; +// CONVERSION_NO_BETTER_THAN_IGNORE_ARGS-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: two[#Numbers#]; +// CONVERSION_NO_BETTER_THAN_IGNORE_ARGS-DAG: Decl[EnumElement]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: three[#Numbers#]; +// CONVERSION_NO_BETTER_THAN_IGNORE_ARGS: End completions + +func testDynamicMemberSubscriptLookup() { + struct MyStruct { + subscript(_ index: Int) -> Int { + return index + } + } + + @dynamicMemberLookup public struct Binding { + subscript(dynamicMember keyPath: WritableKeyPath) -> Binding { + fatalError() + } + } + + struct Foo { + var bar: Binding + + func test(index: Int) { + _ = bar[#^DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP?xfail=rdar90363138^#index] + } + } +} + +// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP: Begin completions +// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: index[#Int#]; name=index +// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; name=keyPath: +// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP: End completions diff --git a/test/IDE/complete_enum_unresolved_dot_argument_labels.swift b/test/IDE/complete_enum_unresolved_dot_argument_labels.swift index cad2f79bd602e..bbdd6b39cc1a1 100644 --- a/test/IDE/complete_enum_unresolved_dot_argument_labels.swift +++ b/test/IDE/complete_enum_unresolved_dot_argument_labels.swift @@ -1,15 +1,55 @@ -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=COMPLETE | %FileCheck %s +// RUN: %empty-directory(%t) +// RUN: %target-swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t enum DragState { case inactive - case dragging(translation: Int) + case dragging(translationX: Int, translationY: Int) + case defaulted(x: Int = 2, y: Int = 3, z: Int, extra: Int) } -func foo() { +func testInit() { var state = DragState.inactive - state = .dragging(#^COMPLETE^# + state = .dragging(#^SIGNATURE^#) + // SIGNATURE: Begin completions, 1 item + // SIGNATURE: Pattern/CurrModule/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#translationX: Int#}, {#translationY: Int#}[')'][#DragState#]; + // SIGNATURE: End completions + + state = .dragging(translationX: 2, #^ARGUMENT^#) + // ARGUMENT: Begin completions, 1 item + // ARGUMENT: Pattern/Local/Flair[ArgLabels]: {#translationY: Int#}[#Int#]; + // ARGUMENT: End completions + + state = .defaulted(#^DEFAULTED^#) + // DEFAULTED: Begin completions, 1 items + // DEFAULTED: Pattern/CurrModule/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#x: Int#}, {#y: Int#}, {#z: Int#}, {#extra: Int#}[')'][#DragState#]; + // DEFAULTED: End completions + + state = .defaulted(x: 1, #^DEFAULTEDARG^#) + state = .defaulted(x: "Wrong type", #^ERRORTYPE?check=DEFAULTEDARG^#) + // DEFAULTEDARG: Begin completions, 2 items + // DEFAULTEDARG: Pattern/Local/Flair[ArgLabels]: {#y: Int#}[#Int#]; + // DEFAULTEDARG: Pattern/Local/Flair[ArgLabels]: {#z: Int#}[#Int#]; + // DEFAULTEDARG: End completions + + state = .defaulted(wrongLabel: 2, #^ERRORARG^#) + // ERRORARG: Begin completions, 3 items + // ERRORARG: Pattern/Local/Flair[ArgLabels]: {#x: Int#}[#Int#]; + // ERRORARG: Pattern/Local/Flair[ArgLabels]: {#y: Int#}[#Int#]; + // ERRORARG: Pattern/Local/Flair[ArgLabels]: {#z: Int#}[#Int#]; } -// CHECK: Begin completions, 1 item -// CHECK: Pattern/CurrModule/Flair[ArgLabels]/TypeRelation[Identical]: ['(']{#translation: Int#}[')'][#DragState#]; -// CHECK: End completions +func testMatch() { + var state = DragState.inactive + let localInt = 42 + switch state { + case .dragging(translationX: 2, #^MATCH_ARGY^#): + // MATCH_ARGY: Begin completions + // FIXME: This should have an identical type relation + // MATCH_ARGY: Decl[LocalVar]/Local/TypeRelation[Identical]: localInt[#Int#]; name=localInt + // FIXME: We should offer 'translationY:' (without any flair since it's optional), `let translationY`, and `_` + // MATCH_ARGY: End completions + break + default: + break + } +} diff --git a/test/IDE/complete_subscript.swift b/test/IDE/complete_subscript.swift index 0e4c527fad4c5..d5ea97656488c 100644 --- a/test/IDE/complete_subscript.swift +++ b/test/IDE/complete_subscript.swift @@ -17,6 +17,7 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=LABELED_SUBSCRIPT | %FileCheck %s -check-prefix=LABELED_SUBSCRIPT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TUPLE | %FileCheck %s -check-prefix=TUPLE +// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=SETTABLE_SUBSCRIPT | %FileCheck %s -check-prefix=SETTABLE_SUBSCRIPT struct MyStruct { static subscript(x: Int, static defValue: T) -> MyStruct { @@ -63,7 +64,7 @@ func test1() { let _ = MyStruct()[#^INSTANCE_INT_BRACKET^# // INSTANCE_INT_BRACKET: Begin completions // INSTANCE_INT_BRACKET-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#(x): Int#}, {#instance: Int#}[']'][#Int#]; -// INSTANCE_INT_BRACKET-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; +// INSTANCE_INT_BRACKET-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; // INSTANCE_INT_BRACKET: End completions } func test2(value: MyStruct) { @@ -89,7 +90,7 @@ func test2(value: MyStruct) { let _ = value[#^INSTANCE_ARCHETYPE_BRACKET^# // INSTANCE_ARCHETYPE_BRACKET: Begin completions // INSTANCE_ARCHETYPE_BRACKET-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#(x): Int#}, {#instance: U#}[']'][#Int#]; -// INSTANCE_ARCHETYPE_BRACKET-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; +// INSTANCE_ARCHETYPE_BRACKET-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; // INSTANCE_ARCHETYPE_BRACKET: End completions let _ = MyStruct[42, #^METATYPE_LABEL^# @@ -116,26 +117,28 @@ class Derived: Base { // SELF_IN_INSTANCEMETHOD: Begin completions, 3 items // SELF_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#derivedInstance: Int#}[']'][#Int#]; // SELF_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/Super/Flair[ArgLabels]: ['[']{#instance: Int#}[']'][#Int#]; -// SELF_IN_INSTANCEMETHOD-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; +// SELF_IN_INSTANCEMETHOD-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; // SELF_IN_INSTANCEMETHOD: End completions let _ = super[#^SUPER_IN_INSTANCEMETHOD^#] // SUPER_IN_INSTANCEMETHOD: Begin completions, 2 items // SUPER_IN_INSTANCEMETHOD-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#instance: Int#}[']'][#Int#]; -// SUPER_IN_INSTANCEMETHOD-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; +// SUPER_IN_INSTANCEMETHOD-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; // SUPER_IN_INSTANCEMETHOD: End completions } static func testStatic() { let _ = self[#^SELF_IN_STATICMETHOD^#] -// SELF_IN_STATICMETHOD: Begin completions, 2 items +// SELF_IN_STATICMETHOD: Begin completions, 3 items // SELF_IN_STATICMETHOD-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#derivedStatic: Int#}[']'][#Int#]; // SELF_IN_STATICMETHOD-DAG: Decl[Subscript]/Super/Flair[ArgLabels]: ['[']{#static: Int#}[']'][#Int#]; +// SELF_IN_STATICMETHOD-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; // SELF_IN_STATICMETHOD: End completions let _ = super[#^SUPER_IN_STATICMETHOD^#] -// SUPER_IN_STATICMETHOD: Begin completions, 1 items +// SUPER_IN_STATICMETHOD: Begin completions, 2 items // SUPER_IN_STATICMETHOD-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#static: Int#}[']'][#Int#]; +// SUPER_IN_STATICMETHOD-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; // SUPER_IN_STATICMETHOD: End completions } } @@ -147,13 +150,30 @@ func testSubscriptCallSig(val: MyStruct1) { val[#^LABELED_SUBSCRIPT^# // LABELED_SUBSCRIPT: Begin completions, 2 items // LABELED_SUBSCRIPT-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#idx1: Int#}, {#idx2: Comparable#}[']'][#Int!#]; -// LABELED_SUBSCRIPT-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; +// LABELED_SUBSCRIPT-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath, Value>#}[']'][#Value#]; // LABELED_SUBSCRIPT: End completions } func testSubcscriptTuple(val: (x: Int, String)) { val[#^TUPLE^#] // TUPLE: Begin completions, 1 items -// TUPLE-DAG: Pattern/CurrModule/Flair[ArgLabels]: ['[']{#keyPath: KeyPath<(x: Int, String), Value>#}[']'][#Value#]; +// TUPLE-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath<(x: Int, String), Value>#}[']'][#Value#]; // TUPLE: End completions } + +struct HasSettableSub { + subscript(a: String) -> Any { + get { return 1 } + set { } + } +} + +func testSettableSub(x: inout HasSettableSub) { + let local = "some string" + x[#^SETTABLE_SUBSCRIPT^#] = 32 +} +// SETTABLE_SUBSCRIPT: Begin completions +// SETTABLE_SUBSCRIPT-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath#}[']'][#Value#]; +// SETTABLE_SUBSCRIPT-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#(a): String#}[']'][#@lvalue Any#]; +// SETTABLE_SUBSCRIPT-DAG: Decl[LocalVar]/Local/TypeRelation[Identical]: local[#String#]; name=local +// SETTABLE_SUBSCRIPT: End completions diff --git a/test/IDE/complete_swift_key_path.swift b/test/IDE/complete_swift_key_path.swift index 8340122ee2186..5e19f81300163 100644 --- a/test/IDE/complete_swift_key_path.swift +++ b/test/IDE/complete_swift_key_path.swift @@ -32,7 +32,9 @@ // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONTEXT_FUNC_INOUT | %FileCheck %s -check-prefix=PERSONTYPE-DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONTEXT_FUNC_VARIADIC | %FileCheck %s -check-prefix=ARRAYTYPE-DOT -// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_KEY_PATH_BASE | %FileCheck %s -check-prefix=PERSONTYPE-DOT +// FIXME: We need the argument after the code completion token to determine the base of the key path in this test case. But since we ignore the types of arguments after the code completion token, we can't determine the base type. +// DISABLED: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_KEY_PATH_BASE | %FileCheck %s -check-prefix=PERSONTYPE-DOT + // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=GENERIC_KEY_PATH_RESULT | %FileCheck %s -check-prefix=PERSONTYPE-DOT // RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=COMPLETE_AFTER_SELF | %FileCheck %s -check-prefix=OBJ-NODOT diff --git a/test/IDE/complete_unresolved_members.swift b/test/IDE/complete_unresolved_members.swift index 0cc2bb602ed85..8fbf88d5045ff 100644 --- a/test/IDE/complete_unresolved_members.swift +++ b/test/IDE/complete_unresolved_members.swift @@ -216,7 +216,18 @@ OptionSetTaker5(.Option1, .Option4, .#^UNRESOLVED_13?check=UNRESOLVED_3^#, .West OptionSetTaker5(.#^UNRESOLVED_14?check=UNRESOLVED_1^#, .Option4, .South, .West) OptionSetTaker5([.#^UNRESOLVED_15?check=UNRESOLVED_1^#], .Option4, .South, .West) -OptionSetTaker6(.#^UNRESOLVED_16?check=UNRESOLVED_4^#, .Option4) +OptionSetTaker6(.#^UNRESOLVED_16^#, .Option4) +// UNRESOLVED_16: Begin completions +// UNRESOLVED_16-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option1[#SomeOptions1#]; +// UNRESOLVED_16-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option2[#SomeOptions1#]; +// UNRESOLVED_16-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option3[#SomeOptions1#]; +// UNRESOLVED_16-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option4[#SomeOptions2#]; +// UNRESOLVED_16-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option5[#SomeOptions2#]; +// UNRESOLVED_16-DAG: Decl[StaticVar]/CurrNominal/Flair[ExprSpecific]/TypeRelation[Identical]: Option6[#SomeOptions2#]; +// UNRESOLVED_16-DAG: Decl[StaticVar]/CurrNominal: NotOption[#Int#]; name=NotOption +// UNRESOLVED_16: End completion + + OptionSetTaker6(.Option4, .#^UNRESOLVED_17?check=UNRESOLVED_4^#,) var a = {() in diff --git a/validation-test/IDE/crashers_2_fixed/0026-completion-type-not-part-of-solution.swift b/validation-test/IDE/crashers_2_fixed/0026-completion-type-not-part-of-solution.swift new file mode 100644 index 0000000000000..28d907854b95d --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0026-completion-type-not-part-of-solution.swift @@ -0,0 +1,17 @@ +// RUN: %swift-ide-test -code-completion -code-completion-token=COMPLETE -source-filename=%s + +struct Image {} + +@resultBuilder struct ViewBuilder2 { + static func buildBlock(_ content: Content) -> Content { fatalError() } +} + +struct NavigationLink { + init(@ViewBuilder2 destination: () -> Destination) + init(_ title: String) +} + +struct CollectionRowView { + func foo() { + NavigationLink() { + Image().#^COMPLETE^# diff --git a/validation-test/IDE/crashers_2_fixed/0027-autoclosure-curry-thunk.swift b/validation-test/IDE/crashers_2_fixed/0027-autoclosure-curry-thunk.swift new file mode 100644 index 0000000000000..07748f564169d --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0027-autoclosure-curry-thunk.swift @@ -0,0 +1,26 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +struct MyView { + func pnAppear(perform action: (() -> Void)) -> MyView { + return self + } + func qadding(_ edges: Int, _ length: Int) -> MyView { + return self + } +} + +@resultBuilder struct ViewBuilder { + static func buildBlock(_ content: MyView) -> MyView { + return content + } +} + +struct AdaptsToSoftwareKeyboard { + @ViewBuilder func body(content: MyView) -> MyView { + content + .qadding(1234567, #^COMPLETE^#0) + .pnAppear(perform: subscribeToKeyboardEvents) + } + + func subscribeToKeyboardEvents() {} +} diff --git a/validation-test/IDE/crashers_2_fixed/0028-member-reference-error-type.swift b/validation-test/IDE/crashers_2_fixed/0028-member-reference-error-type.swift new file mode 100644 index 0000000000000..cc4108a7e70eb --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0028-member-reference-error-type.swift @@ -0,0 +1,9 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +struct PopularityBadge { + init( + + +#if DEBUG +var previews { + PopularityBadge(score: #^COMPLETE^# diff --git a/validation-test/IDE/crashers_2_fixed/0029-closure-implicit-return.swift b/validation-test/IDE/crashers_2_fixed/0029-closure-implicit-return.swift new file mode 100644 index 0000000000000..39d5d8d5bd654 --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0029-closure-implicit-return.swift @@ -0,0 +1,32 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +public enum MyEnum { + case one + case two +} + +struct MyStruct { + func takesClosure(_ action: (() -> Void)) -> MyStruct { + return self + } + + func generic(_ keyPath: V, _ value: MyEnum) -> MyStruct { + return self + } +} + +@resultBuilder struct MyBuilder { + static func buildBlock(_ content: MyStruct) -> MyStruct { + return content + } +} + +struct ItemDetailView { + @MyBuilder var body: MyStruct { + MyStruct() + .generic("xorizontalSizeClass", .#^COMPLETE^#regular) + .takesClosure { + "abc" + } + } +} diff --git a/validation-test/IDE/crashers_2_fixed/0030-arg-completion-no-locator.swift b/validation-test/IDE/crashers_2_fixed/0030-arg-completion-no-locator.swift new file mode 100644 index 0000000000000..13409d6d32add --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0030-arg-completion-no-locator.swift @@ -0,0 +1,15 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +@dynamicMemberLookup public struct Binding { + subscript(dynamicMember keyPath: WritableKeyPath) -> Binding { + fatalError() + } +} + +struct Foo { + var bar: Binding<[String]> + + func test(index: Int) { + _ = bar[#^COMPLETE^#index] + } +} diff --git a/validation-test/IDE/crashers_2_fixed/0032-constructor-call-in-result-builder.swift b/validation-test/IDE/crashers_2_fixed/0032-constructor-call-in-result-builder.swift new file mode 100644 index 0000000000000..e487ed2bbbcd6 --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0032-constructor-call-in-result-builder.swift @@ -0,0 +1,15 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +@resultBuilder public struct ViewBuilder { + static func buildBlock(_ content: TextField) -> TextField { fatalError() } +} + +struct TextField { + init(_ title: String, text: String) {} +} + +struct EncodedView { + @ViewBuilder var body: TextField { + TextField("Encoded", #^COMPLETE^#) + } +} diff --git a/validation-test/IDE/crashers_2_fixed/0033-dont-optimize-constraints-for-ignored-args.swift b/validation-test/IDE/crashers_2_fixed/0033-dont-optimize-constraints-for-ignored-args.swift new file mode 100644 index 0000000000000..c5db6d2696700 --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0033-dont-optimize-constraints-for-ignored-args.swift @@ -0,0 +1,7 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +func foo() { + bar { + .dragging(arg1: #^COMPLETE^#, arg2: drag ?? .zero) + } +} diff --git a/validation-test/IDE/crashers_2_fixed/0033-trailing-closure-arg-label-matching.swift b/validation-test/IDE/crashers_2_fixed/0033-trailing-closure-arg-label-matching.swift new file mode 100644 index 0000000000000..a54abec7a41fd --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0033-trailing-closure-arg-label-matching.swift @@ -0,0 +1,11 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE | %FileCheck %s + +func foo() { + _ = sink { items in }#^COMPLETE^# +} + +func sink(receiveCompletion: (Int) -> Void, receiveValue: (Int) -> Void) { fatalError() } + +// CHECK: Begin completions, 1 items +// CHECK-DAG: Pattern/Local/Flair[ArgLabels]: {#receiveValue: (Int) -> Void {<#Int#> in|}#}[#(Int) -> Void#]; +// CHECK: End completions From e63a064dab0807dba21737ada8c5e0b10b9622ab Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Thu, 17 Mar 2022 18:08:53 +0100 Subject: [PATCH 176/242] [build-presets] Disable SourceKit-LSP tests on Linux SourceKit-LSP tests are flaky on Linux, disable them until we have fixed the root cause. Resolves rdar://89359439 --- utils/build-presets.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 8a4091acff347..08551a0ded450 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -876,6 +876,8 @@ foundation libdispatch indexstore-db sourcekit-lsp +# SourceKit-LSP tests are flaky on Linux, disable them until we have fixed the root cause - rdar://90437872 +skip-test-sourcekit-lsp swiftdocc lit-args=-v --time-tests From 9d52099d5b1fedc3ba367d88b01971939ce4e8cb Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Tue, 15 Mar 2022 19:26:36 -0700 Subject: [PATCH 177/242] [cxx-interop] start to emitting a unified header file for a Swift module This change removes the -emit-cxx-header option, and adds a new -emit-clang-header-path option instead. It's aliased to -emit-objc-header-path for now, but in the future, -emit-objc-header-path will alias to it. After this change Swift can start emitting a single header file that can be expose declarations to C, Objective-C, or C++. For now C++ interface is generated (for all public decls) only when -enable-cxx-interop flag is passed, but that behavior will change once attribute is supported. --- include/swift/AST/DiagnosticsFrontend.def | 4 +- include/swift/Basic/FileTypes.def | 2 +- .../swift/Basic/SupplementaryOutputPaths.h | 29 ++---- include/swift/Frontend/Frontend.h | 3 +- .../swift/Frontend/FrontendInputsAndOutputs.h | 3 +- include/swift/Option/Options.td | 10 +- include/swift/PrintAsClang/PrintAsClang.h | 16 ++-- lib/Basic/FileTypes.cpp | 6 +- lib/Driver/Driver.cpp | 12 +-- lib/Driver/ToolChains.cpp | 10 +- .../ArgsToFrontendOptionsConverter.cpp | 7 +- .../ArgsToFrontendOutputsConverter.cpp | 50 ++++------ lib/Frontend/Frontend.cpp | 9 +- lib/Frontend/FrontendInputsAndOutputs.cpp | 12 +-- lib/Frontend/FrontendOptions.cpp | 3 +- lib/FrontendTool/FrontendTool.cpp | 40 ++------ lib/PrintAsClang/PrintAsClang.cpp | 91 ++++++++++--------- test/Driver/bridging-pch.swift | 6 +- test/Driver/parseable_output.swift | 2 +- test/Driver/parseable_output_unicode.swift | 2 +- .../supplementary-output-support.swift | 13 +-- .../SwiftToCxx/functions/cdecl-execution.cpp | 2 +- test/Interop/SwiftToCxx/functions/cdecl.swift | 2 +- .../functions/function-availability.swift | 2 +- .../functions/swift-functions-execution.cpp | 2 +- .../functions/swift-functions.swift | 2 +- .../module/module-to-namespace.swift | 2 +- test/PrintAsCxx/empty.swift | 24 +++-- .../Inputs/comments-expected-output.h | 3 + 29 files changed, 157 insertions(+), 212 deletions(-) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index f470961abdb0c..b9e7c43b37aac 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -118,9 +118,7 @@ ERROR(error_mode_cannot_emit_dependencies,none, ERROR(error_mode_cannot_emit_reference_dependencies,none, "this mode does not support emitting reference dependency files", ()) ERROR(error_mode_cannot_emit_header,none, - "this mode does not support emitting Objective-C headers", ()) -ERROR(error_mode_cannot_emit_cxx_header,none, - "this mode does not support emitting C++ headers", ()) + "this mode does not support emitting Objective-C or C++ headers", ()) ERROR(error_mode_cannot_emit_loaded_module_trace,none, "this mode does not support emitting the loaded module trace", ()) ERROR(error_mode_cannot_emit_module,none, diff --git a/include/swift/Basic/FileTypes.def b/include/swift/Basic/FileTypes.def index 074df9e4b6ab1..2d3c7e3754332 100644 --- a/include/swift/Basic/FileTypes.def +++ b/include/swift/Basic/FileTypes.def @@ -59,7 +59,7 @@ TYPE("raw-sib", RawSIB, "sib", "") TYPE("llvm-ir", LLVM_IR, "ll", "") TYPE("llvm-bc", LLVM_BC, "bc", "") TYPE("diagnostics", SerializedDiagnostics, "dia", "") -TYPE("objc-header", ObjCHeader, "h", "") +TYPE("clang-header", ClangHeader, "h", "") TYPE("swift-dependencies", SwiftDeps, "swiftdeps", "") TYPE("external-swift-dependencies", ExternalSwiftDeps, "swiftdeps.external", "") TYPE("remap", Remapping, "remap", "") diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index 68eb39d3d8fba..c8ba0b075ba0e 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -20,7 +20,8 @@ namespace swift { struct SupplementaryOutputPaths { - /// The path to which we should emit an Objective-C header for the module. + /// The path to which we should emit a header file that exposes the Swift + /// declarations to C, Objective-C and C++ clients for the module. /// /// Currently only makes sense when the compiler has whole module knowledge. /// The modes for which it makes sense incuide both WMO and the "merge @@ -28,19 +29,8 @@ struct SupplementaryOutputPaths { /// the header is emitted in single-file mode, since it needs whole-module /// information. /// - /// \sa swift::printAsObjC - std::string ObjCHeaderOutputPath; - - /// The path to which we should emit a C++ header for the module. - /// - /// Currently only makes sense when the compiler has whole module knowledge. - /// The modes for which it makes sense include both WMO and the "merge - /// modules" job that happens after the normal compilation jobs. That's where - /// the header is emitted in single-file mode, since it needs whole-module - /// information. - /// - /// \sa swift::printAsCXX - std::string CxxHeaderOutputPath; + /// \sa swift::printAsClangHeader + std::string ClangHeaderOutputPath; /// The path to which we should emit a serialized module. /// It is valid whenever there are any inputs. @@ -170,10 +160,8 @@ struct SupplementaryOutputPaths { /// Apply a given function for each existing (non-empty string) supplementary output void forEachSetOutput(llvm::function_ref fn) const { - if (!ObjCHeaderOutputPath.empty()) - fn(ObjCHeaderOutputPath); - if (!CxxHeaderOutputPath.empty()) - fn(CxxHeaderOutputPath); + if (!ClangHeaderOutputPath.empty()) + fn(ClangHeaderOutputPath); if (!ModuleOutputPath.empty()) fn(ModuleOutputPath); if (!ModuleSourceInfoOutputPath.empty()) @@ -209,9 +197,8 @@ struct SupplementaryOutputPaths { } bool empty() const { - return ObjCHeaderOutputPath.empty() && CxxHeaderOutputPath.empty() && - ModuleOutputPath.empty() && ModuleDocOutputPath.empty() && - DependenciesFilePath.empty() && + return ClangHeaderOutputPath.empty() && ModuleOutputPath.empty() && + ModuleDocOutputPath.empty() && DependenciesFilePath.empty() && ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 93bfe7376f7b5..2a35e7c822a15 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -392,8 +392,7 @@ class CompilerInvocation { std::string getOutputFilenameForAtMostOnePrimary() const; std::string getMainInputFilenameForDebugInfoForAtMostOnePrimary() const; - std::string getObjCHeaderOutputPathForAtMostOnePrimary() const; - std::string getCxxHeaderOutputPathForAtMostOnePrimary() const; + std::string getClangHeaderOutputPathForAtMostOnePrimary() const; std::string getModuleOutputPathForAtMostOnePrimary() const; std::string getReferenceDependenciesFilePathForPrimary(StringRef filename) const; diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index 572f3a1df17b4..c28810fb5c84f 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -249,8 +249,7 @@ class FrontendInputsAndOutputs { bool hasDependenciesPath() const; bool hasReferenceDependenciesPath() const; - bool hasObjCHeaderOutputPath() const; - bool hasCxxHeaderOutputPath() const; + bool hasClangHeaderOutputPath() const; bool hasLoadedModuleTracePath() const; bool hasModuleOutputPath() const; bool hasModuleDocOutputPath() const; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index e8787c5074617..faae3e2a0db28 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -532,13 +532,11 @@ def emit_objc_header_path : Separate<["-"], "emit-objc-header-path">, SupplementaryOutput]>, MetaVarName<"">, HelpText<"Emit an Objective-C header file to ">; -def emit_cxx_header : Flag<["-"], "emit-cxx-header">, - Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>, - HelpText<"Emit a C++ header file">; -def emit_cxx_header_path : Separate<["-"], "emit-cxx-header-path">, - Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath, +def emit_clang_header_path : Separate<["-"], "emit-clang-header-path">, + Flags<[FrontendOption, NoDriverOption, NoInteractiveOption, ArgumentIsPath, SupplementaryOutput]>, - MetaVarName<"">, HelpText<"Emit a C++ header file to ">; + HelpText<"Emit an Objective-C and C++ header file to ">, + Alias; def static : Flag<["-"], "static">, Flags<[FrontendOption, ModuleInterfaceOption, NoInteractiveOption]>, diff --git a/include/swift/PrintAsClang/PrintAsClang.h b/include/swift/PrintAsClang/PrintAsClang.h index 867f75fd245e8..a52699b330dc4 100644 --- a/include/swift/PrintAsClang/PrintAsClang.h +++ b/include/swift/PrintAsClang/PrintAsClang.h @@ -21,16 +21,18 @@ namespace swift { class ModuleDecl; class ValueDecl; - /// Print the Objective-C-compatible declarations in a module as a Clang - /// header. + /// Print the exposed declarations in a module into a Clang header. /// - /// Returns true on error. - bool printAsObjC(raw_ostream &out, ModuleDecl *M, StringRef bridgingHeader); - - /// Print the C++-compatible declarations in a module as a Clang header. + /// The Objective-C compatible declarations are printed into a block that + /// ensures that those declarations are only usable when the header is + /// compiled in Objective-C mode. + /// The C++ compatible declarations are printed into a block that ensures + /// that those declarations are only usable when the header is compiled in + /// C++ mode. /// /// Returns true on error. - bool printAsCXX(raw_ostream &os, ModuleDecl *M); + bool printAsClangHeader(raw_ostream &out, ModuleDecl *M, + StringRef bridgingHeader); } #endif diff --git a/lib/Basic/FileTypes.cpp b/lib/Basic/FileTypes.cpp index 58db293c5d090..607c4cece5879 100644 --- a/lib/Basic/FileTypes.cpp +++ b/lib/Basic/FileTypes.cpp @@ -73,7 +73,7 @@ bool file_types::isTextual(ID Id) { case file_types::TY_ASTDump: case file_types::TY_RawSIL: case file_types::TY_LLVM_IR: - case file_types::TY_ObjCHeader: + case file_types::TY_ClangHeader: case file_types::TY_AutolinkFile: case file_types::TY_ImportedModules: case file_types::TY_TBD: @@ -131,7 +131,7 @@ bool file_types::isAfterLLVM(ID Id) { case file_types::TY_Dependencies: case file_types::TY_ASTDump: case file_types::TY_RawSIL: - case file_types::TY_ObjCHeader: + case file_types::TY_ClangHeader: case file_types::TY_AutolinkFile: case file_types::TY_Image: case file_types::TY_dSYM: @@ -181,7 +181,7 @@ bool file_types::isPartOfSwiftCompilation(ID Id) { case file_types::TY_LLVM_BC: case file_types::TY_Object: case file_types::TY_Dependencies: - case file_types::TY_ObjCHeader: + case file_types::TY_ClangHeader: case file_types::TY_AutolinkFile: case file_types::TY_PCH: case file_types::TY_ImportedModules: diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index f513171f82bd5..a7359bb9c9ac9 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1981,7 +1981,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, if (Arg *A = Args.getLastArg(options::OPT_import_objc_header)) { StringRef Value = A->getValue(); auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value)); - if (Ty == file_types::TY_ObjCHeader) { + if (Ty == file_types::TY_ClangHeader) { auto *HeaderInput = C.createAction(*A, Ty); StringRef PersistentPCHDir; if (const Arg *A = Args.getLastArg(options::OPT_pch_output_dir)) { @@ -2064,7 +2064,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, case file_types::TY_LLVM_IR: case file_types::TY_LLVM_BC: case file_types::TY_SerializedDiagnostics: - case file_types::TY_ObjCHeader: + case file_types::TY_ClangHeader: case file_types::TY_ClangModuleFile: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -3478,12 +3478,12 @@ void Driver::chooseObjectiveCHeaderOutputPath(Compilation &C, StringRef workingDirectory, CommandOutput *Output) const { - if (hasExistingAdditionalOutput(*Output, file_types::TY_ObjCHeader)) + if (hasExistingAdditionalOutput(*Output, file_types::TY_ClangHeader)) return; StringRef ObjCHeaderPath; if (OutputMap) { - auto iter = OutputMap->find(file_types::TY_ObjCHeader); + auto iter = OutputMap->find(file_types::TY_ClangHeader); if (iter != OutputMap->end()) ObjCHeaderPath = iter->second; } @@ -3493,13 +3493,13 @@ void Driver::chooseObjectiveCHeaderOutputPath(Compilation &C, ObjCHeaderPath = A->getValue(); if (!ObjCHeaderPath.empty()) { - Output->setAdditionalOutputForType(file_types::TY_ObjCHeader, + Output->setAdditionalOutputForType(file_types::TY_ClangHeader, ObjCHeaderPath); } else { // Put the header next to the primary output file. // FIXME: That's not correct if the user /just/ passed -emit-header // and not -emit-module. - addAuxiliaryOutput(C, *Output, file_types::TY_ObjCHeader, + addAuxiliaryOutput(C, *Output, file_types::TY_ClangHeader, /*output file map*/ nullptr, workingDirectory); } } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 40bbcc7b1aed1..62b6ddc211d5f 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -675,7 +675,7 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const { case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: case file_types::TY_SerializedDiagnostics: - case file_types::TY_ObjCHeader: + case file_types::TY_ClangHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -814,7 +814,7 @@ void ToolChain::JobContext::addFrontendSupplementaryOutputArguments( file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); - if (addOutputsOfType(arguments, Output, Args, file_types::ID::TY_ObjCHeader, + if (addOutputsOfType(arguments, Output, Args, file_types::ID::TY_ClangHeader, "-emit-objc-header-path")) { assert(OI.CompilerMode == OutputInfo::Mode::SingleCompile && "The Swift tool should only emit an Obj-C header in single compile" @@ -935,7 +935,7 @@ ToolChain::constructInvocation(const BackendJobAction &job, case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: case file_types::TY_SerializedDiagnostics: - case file_types::TY_ObjCHeader: + case file_types::TY_ClangHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -1098,7 +1098,7 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); addOutputsOfType(Arguments, context.Output, context.Args, - file_types::TY_ObjCHeader, "-emit-objc-header-path"); + file_types::TY_ClangHeader, "-emit-objc-header-path"); addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_TBD, "-emit-tbd-path"); @@ -1307,7 +1307,7 @@ ToolChain::constructInvocation(const GeneratePCHJobAction &job, file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); - addInputsOfType(Arguments, context.InputActions, file_types::TY_ObjCHeader); + addInputsOfType(Arguments, context.InputActions, file_types::TY_ClangHeader); context.Args.AddLastArg(Arguments, options::OPT_index_store_path); if (job.isPersistentPCH()) { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 0526af38d1a3f..02071f42c0c8d 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -625,15 +625,10 @@ bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths() return true; } if (!FrontendOptions::canActionEmitClangHeader(Opts.RequestedAction) && - Opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { + Opts.InputsAndOutputs.hasClangHeaderOutputPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_header); return true; } - if (!FrontendOptions::canActionEmitClangHeader(Opts.RequestedAction) && - Opts.InputsAndOutputs.hasCxxHeaderOutputPath()) { - Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_cxx_header); - return true; - } if (!FrontendOptions::canActionEmitLoadedModuleTrace(Opts.RequestedAction) && Opts.InputsAndOutputs.hasLoadedModuleTracePath()) { Diags.diagnose(SourceLoc(), diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index d50ad19069296..1ebaa9837f3cd 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -308,10 +308,8 @@ Optional> SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() const { - auto objCHeaderOutput = getSupplementaryFilenamesFromArguments( + auto clangHeaderOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_objc_header_path); - auto cxxHeaderOutput = - getSupplementaryFilenamesFromArguments(options::OPT_emit_cxx_header_path); auto moduleOutput = getSupplementaryFilenamesFromArguments(options::OPT_emit_module_path); auto moduleDocOutput = @@ -341,8 +339,8 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_module_semantic_info_path); auto optRecordOutput = getSupplementaryFilenamesFromArguments( options::OPT_save_optimization_record_path); - if (!objCHeaderOutput || !cxxHeaderOutput || !moduleOutput || - !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || + if (!clangHeaderOutput || !moduleOutput || !moduleDocOutput || + !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || !moduleInterfaceOutput || !privateModuleInterfaceOutput || !moduleSourceInfoOutput || !moduleSummaryOutput || !abiDescriptorOutput || @@ -355,8 +353,7 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() InputsAndOutputs.countOfFilesProducingSupplementaryOutput(); for (unsigned i = 0; i < N; ++i) { SupplementaryOutputPaths sop; - sop.ObjCHeaderOutputPath = (*objCHeaderOutput)[i]; - sop.CxxHeaderOutputPath = (*cxxHeaderOutput)[i]; + sop.ClangHeaderOutputPath = (*clangHeaderOutput)[i]; sop.ModuleOutputPath = (*moduleOutput)[i]; sop.ModuleDocOutputPath = (*moduleDocOutput)[i]; sop.DependenciesFilePath = (*dependenciesFile)[i]; @@ -437,14 +434,9 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( // There is no non-path form of -emit-fixits-path auto fixItsOutputPath = pathsFromArguments.FixItsOutputPath; - auto objcHeaderOutputPath = determineSupplementaryOutputFilename( - OPT_emit_objc_header, pathsFromArguments.ObjCHeaderOutputPath, - file_types::TY_ObjCHeader, "", - defaultSupplementaryOutputPathExcludingExtension); - - auto cxxHeaderOutputPath = determineSupplementaryOutputFilename( - OPT_emit_cxx_header, pathsFromArguments.CxxHeaderOutputPath, - file_types::TY_ObjCHeader, "", + auto clangHeaderOutputPath = determineSupplementaryOutputFilename( + OPT_emit_objc_header, pathsFromArguments.ClangHeaderOutputPath, + file_types::TY_ClangHeader, "", defaultSupplementaryOutputPathExcludingExtension); auto loadedModuleTracePath = determineSupplementaryOutputFilename( @@ -500,8 +492,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( defaultSupplementaryOutputPathExcludingExtension); SupplementaryOutputPaths sop; - sop.ObjCHeaderOutputPath = objcHeaderOutputPath; - sop.CxxHeaderOutputPath = cxxHeaderOutputPath; + sop.ClangHeaderOutputPath = clangHeaderOutputPath; sop.ModuleOutputPath = moduleOutputPath; sop.ModuleDocOutputPath = moduleDocOutputPath; sop.DependenciesFilePath = dependenciesFilePath; @@ -586,8 +577,7 @@ createFromTypeToPathMap(const TypeToPathMap *map) { if (!map) return paths; const std::pair typesAndStrings[] = { - {file_types::TY_ObjCHeader, paths.ObjCHeaderOutputPath}, - {file_types::TY_ObjCHeader, paths.CxxHeaderOutputPath}, + {file_types::TY_ClangHeader, paths.ClangHeaderOutputPath}, {file_types::TY_SwiftModuleFile, paths.ModuleOutputPath}, {file_types::TY_SwiftModuleDocFile, paths.ModuleDocOutputPath}, {file_types::TY_SwiftSourceInfoFile, paths.ModuleSourceInfoOutputPath}, @@ -615,17 +605,17 @@ createFromTypeToPathMap(const TypeToPathMap *map) { Optional> SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const { - if (Arg *A = Args.getLastArg( - options::OPT_emit_objc_header_path, options::OPT_emit_cxx_header_path, - options::OPT_emit_module_path, options::OPT_emit_module_doc_path, - options::OPT_emit_dependencies_path, - options::OPT_emit_reference_dependencies_path, - options::OPT_serialize_diagnostics_path, - options::OPT_emit_loaded_module_trace_path, - options::OPT_emit_module_interface_path, - options::OPT_emit_private_module_interface_path, - options::OPT_emit_module_source_info_path, - options::OPT_emit_tbd_path)) { + if (Arg *A = Args.getLastArg(options::OPT_emit_objc_header_path, + options::OPT_emit_module_path, + options::OPT_emit_module_doc_path, + options::OPT_emit_dependencies_path, + options::OPT_emit_reference_dependencies_path, + options::OPT_serialize_diagnostics_path, + options::OPT_emit_loaded_module_trace_path, + options::OPT_emit_module_interface_path, + options::OPT_emit_private_module_interface_path, + options::OPT_emit_module_source_info_path, + options::OPT_emit_tbd_path)) { Diags.diagnose(SourceLoc(), diag::error_cannot_have_supplementary_outputs, A->getSpelling(), "-supplementary-output-file-map"); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index f8a5ee1b462ed..2fe54499d1fdc 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -92,14 +92,9 @@ CompilerInvocation::getMainInputFilenameForDebugInfoForAtMostOnePrimary() .MainInputFilenameForDebugInfo; } std::string -CompilerInvocation::getObjCHeaderOutputPathForAtMostOnePrimary() const { +CompilerInvocation::getClangHeaderOutputPathForAtMostOnePrimary() const { return getPrimarySpecificPathsForAtMostOnePrimary() - .SupplementaryOutputs.ObjCHeaderOutputPath; -} -std::string -CompilerInvocation::getCxxHeaderOutputPathForAtMostOnePrimary() const { - return getPrimarySpecificPathsForAtMostOnePrimary() - .SupplementaryOutputs.CxxHeaderOutputPath; + .SupplementaryOutputs.ClangHeaderOutputPath; } std::string CompilerInvocation::getModuleOutputPathForAtMostOnePrimary() const { return getPrimarySpecificPathsForAtMostOnePrimary() diff --git a/lib/Frontend/FrontendInputsAndOutputs.cpp b/lib/Frontend/FrontendInputsAndOutputs.cpp index 59d12af5c142a..cb6b4ad2dc515 100644 --- a/lib/Frontend/FrontendInputsAndOutputs.cpp +++ b/lib/Frontend/FrontendInputsAndOutputs.cpp @@ -213,7 +213,7 @@ bool FrontendInputsAndOutputs::shouldTreatAsObjCHeader() const { if (hasSingleInput()) { StringRef InputExt = llvm::sys::path::extension(getFilenameOfFirstInput()); switch (file_types::lookupTypeForExtension(InputExt)) { - case file_types::TY_ObjCHeader: + case file_types::TY_ClangHeader: return true; default: return false; @@ -461,16 +461,10 @@ bool FrontendInputsAndOutputs::hasReferenceDependenciesPath() const { return outs.ReferenceDependenciesFilePath; }); } -bool FrontendInputsAndOutputs::hasObjCHeaderOutputPath() const { +bool FrontendInputsAndOutputs::hasClangHeaderOutputPath() const { return hasSupplementaryOutputPath( [](const SupplementaryOutputPaths &outs) -> const std::string & { - return outs.ObjCHeaderOutputPath; - }); -} -bool FrontendInputsAndOutputs::hasCxxHeaderOutputPath() const { - return hasSupplementaryOutputPath( - [](const SupplementaryOutputPaths &outs) -> const std::string & { - return outs.CxxHeaderOutputPath; + return outs.ClangHeaderOutputPath; }); } bool FrontendInputsAndOutputs::hasLoadedModuleTracePath() const { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index d956b71d203c1..84ecf13f446bb 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -234,8 +234,7 @@ void FrontendOptions::forAllOutputPaths( const std::string *outputs[] = { &outs.ModuleOutputPath, &outs.ModuleDocOutputPath, &outs.ModuleInterfaceOutputPath, &outs.PrivateModuleInterfaceOutputPath, - &outs.ObjCHeaderOutputPath, &outs.CxxHeaderOutputPath, - &outs.ModuleSourceInfoOutputPath}; + &outs.ClangHeaderOutputPath, &outs.ModuleSourceInfoOutputPath}; for (const std::string *next : outputs) { if (!next->empty()) fn(*next); diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 095c3662e4084..9317a20864f24 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -170,36 +170,22 @@ static bool writeSIL(SILModule &SM, const PrimarySpecificPaths &PSPs, /// Prints the Objective-C "generated header" interface for \p M to \p /// outputPath. +/// Print the exposed "generated header" interface for \p M to \p +/// outputPath. /// /// ...unless \p outputPath is empty, in which case it does nothing. /// /// \returns true if there were any errors /// -/// \see swift::printAsObjC -static bool printAsObjCIfNeeded(StringRef outputPath, ModuleDecl *M, - StringRef bridgingHeader) { +/// \see swift::printAsClangHeader +static bool printAsClangHeaderIfNeeded(StringRef outputPath, ModuleDecl *M, + StringRef bridgingHeader) { if (outputPath.empty()) return false; return withOutputFile(M->getDiags(), outputPath, [&](raw_ostream &out) -> bool { - return printAsObjC(out, M, bridgingHeader); - }); -} - -/// Prints the C++ "generated header" interface for \p M to \p -/// outputPath. -/// -/// ...unless \p outputPath is empty, in which case it does nothing. -/// -/// \returns true if there were any errors -/// -/// \see swift::printAsCXX -static bool printAsCxxIfNeeded(StringRef outputPath, ModuleDecl *M) { - if (outputPath.empty()) - return false; - return withOutputFile( - M->getDiags(), outputPath, - [&](raw_ostream &os) -> bool { return printAsCXX(os, M); }); + return printAsClangHeader(out, M, bridgingHeader); + }); } /// Prints the stable module interface for \p M to \p outputPath. @@ -824,7 +810,7 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( bool hadAnyError = false; if ((!Context.hadError() || opts.AllowModuleWithCompilerErrors) && - opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { + opts.InputsAndOutputs.hasClangHeaderOutputPath()) { std::string BridgingHeaderPathForPrint; if (!opts.ImplicitObjCHeaderPath.empty()) { if (opts.BridgingHeaderDirForPrint.hasValue()) { @@ -838,16 +824,10 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( BridgingHeaderPathForPrint = opts.ImplicitObjCHeaderPath; } } - hadAnyError |= printAsObjCIfNeeded( - Invocation.getObjCHeaderOutputPathForAtMostOnePrimary(), + hadAnyError |= printAsClangHeaderIfNeeded( + Invocation.getClangHeaderOutputPathForAtMostOnePrimary(), Instance.getMainModule(), BridgingHeaderPathForPrint); } - if ((!Context.hadError() || opts.AllowModuleWithCompilerErrors) && - opts.InputsAndOutputs.hasCxxHeaderOutputPath()) { - hadAnyError |= printAsCxxIfNeeded( - Invocation.getCxxHeaderOutputPathForAtMostOnePrimary(), - Instance.getMainModule()); - } // Only want the header if there's been any errors, ie. there's not much // point outputting a swiftinterface for an invalid module diff --git a/lib/PrintAsClang/PrintAsClang.cpp b/lib/PrintAsClang/PrintAsClang.cpp index f6910759a596e..116067978d1e1 100644 --- a/lib/PrintAsClang/PrintAsClang.cpp +++ b/lib/PrintAsClang/PrintAsClang.cpp @@ -26,28 +26,32 @@ using namespace swift; +static void emitCxxConditional(raw_ostream &out, + llvm::function_ref cxxCase, + llvm::function_ref cCase = {}) { + out << "#if defined(__cplusplus)\n"; + cxxCase(); + if (cCase) { + out << "#else\n"; + cCase(); + } + out << "#endif\n"; +} + +static void emitObjCConditional(raw_ostream &out, + llvm::function_ref objcCase, + llvm::function_ref nonObjCCase = {}) { + out << "#if defined(__OBJC__)\n"; + objcCase(); + if (nonObjCCase) { + out << "#else\n"; + nonObjCCase(); + } + out << "#endif\n"; +} + static void writePrologue(raw_ostream &out, ASTContext &ctx, StringRef macroGuard) { - auto emitCxxConditional = [&](llvm::function_ref cxxCase, - llvm::function_ref cCase = {}) { - out << "#if defined(__cplusplus)\n"; - cxxCase(); - if (cCase) { - out << "#else\n"; - cCase(); - } - out << "#endif\n"; - }; - auto emitObjCConditional = [&](llvm::function_ref objcCase, - llvm::function_ref nonObjCCase = {}) { - out << "#if defined(__OBJC__)\n"; - objcCase(); - if (nonObjCCase) { - out << "#else\n"; - nonObjCCase(); - } - out << "#endif\n"; - }; out << "// Generated by " << version::getSwiftFullVersion(ctx.LangOpts.EffectiveLanguageVersion) @@ -77,8 +81,10 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, "#endif\n" "\n" "#pragma clang diagnostic ignored \"-Wauto-import\"\n"; - emitObjCConditional([&] { out << "#include \n"; }); + emitObjCConditional(out, + [&] { out << "#include \n"; }); emitCxxConditional( + out, [&] { out << "#include \n" "#include \n" @@ -276,7 +282,7 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, "#else\n" "# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)\n" "#endif\n"; - emitObjCConditional([&] { + emitObjCConditional(out, [&] { out << "#if !defined(IBSegueAction)\n" "# define IBSegueAction\n" "#endif\n"; @@ -295,8 +301,9 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, }; emitMacro("SWIFT_CALL", "__attribute__((swiftcall))"); // SWIFT_NOEXCEPT applies 'noexcept' in C++ mode only. - emitCxxConditional([&] { emitMacro("SWIFT_NOEXCEPT", "noexcept"); }, - [&] { emitMacro("SWIFT_NOEXCEPT"); }); + emitCxxConditional( + out, [&] { emitMacro("SWIFT_NOEXCEPT", "noexcept"); }, + [&] { emitMacro("SWIFT_NOEXCEPT"); }); static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, "need to add SIMD typedefs here if max elements is increased"); } @@ -442,33 +449,33 @@ static std::string computeMacroGuard(const ModuleDecl *M) { return (llvm::Twine(M->getNameStr().upper()) + "_SWIFT_H").str(); } -bool swift::printAsObjC(raw_ostream &os, ModuleDecl *M, - StringRef bridgingHeader) { - llvm::PrettyStackTraceString trace("While generating Objective-C header"); - +static std::string getModuleContentsCxxString(ModuleDecl &M) { SmallPtrSet imports; std::string moduleContentsBuf; llvm::raw_string_ostream moduleContents{moduleContentsBuf}; - printModuleContentsAsObjC(moduleContents, imports, *M); - writePrologue(os, M->getASTContext(), computeMacroGuard(M)); - writeImports(os, imports, *M, bridgingHeader); - writePostImportPrologue(os, *M); - os << moduleContents.str(); - writeEpilogue(os); - - return false; + printModuleContentsAsCxx(moduleContents, imports, M); + return moduleContents.str(); } -bool swift::printAsCXX(raw_ostream &os, ModuleDecl *M) { - llvm::PrettyStackTraceString trace("While generating C++ header"); +bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M, + StringRef bridgingHeader) { + llvm::PrettyStackTraceString trace("While generating Clang header"); SmallPtrSet imports; - std::string moduleContentsBuf; - llvm::raw_string_ostream moduleContents{moduleContentsBuf}; - printModuleContentsAsCxx(moduleContents, imports, *M); + std::string objcModuleContentsBuf; + llvm::raw_string_ostream objcModuleContents{objcModuleContentsBuf}; + printModuleContentsAsObjC(objcModuleContents, imports, *M); writePrologue(os, M->getASTContext(), computeMacroGuard(M)); + emitObjCConditional(os, + [&] { writeImports(os, imports, *M, bridgingHeader); }); writePostImportPrologue(os, *M); - os << moduleContents.str(); + emitObjCConditional(os, [&] { os << objcModuleContents.str(); }); + emitCxxConditional(os, [&] { + // FIXME: Expose Swift with @expose by default. + if (M->getASTContext().LangOpts.EnableCXXInterop) { + os << getModuleContentsCxxString(*M); + } + }); writeEpilogue(os); return false; diff --git a/test/Driver/bridging-pch.swift b/test/Driver/bridging-pch.swift index 4e585f74d7592..8750a013d2e64 100644 --- a/test/Driver/bridging-pch.swift +++ b/test/Driver/bridging-pch.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift -typecheck -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=YESPCHACT -// YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header +// YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header // YESPCHACT: 1: generate-pch, {0}, pch // YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift // YESPCHACT: 3: compile, {2, 1}, none @@ -30,13 +30,13 @@ // Test persistent PCH // RUN: %target-build-swift -typecheck -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACT -// PERSISTENT-YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header +// PERSISTENT-YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header // PERSISTENT-YESPCHACT: 1: generate-pch, {0}, none // PERSISTENT-YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift // PERSISTENT-YESPCHACT: 3: compile, {2, 1}, none // RUN: %target-build-swift -c -driver-print-actions -embed-bitcode -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACTBC -// PERSISTENT-YESPCHACTBC: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header +// PERSISTENT-YESPCHACTBC: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header // PERSISTENT-YESPCHACTBC: 1: generate-pch, {0}, none // PERSISTENT-YESPCHACTBC: 2: input, "{{.*}}bridging-pch.swift", swift // PERSISTENT-YESPCHACTBC: 3: compile, {2, 1}, llvm-bc diff --git a/test/Driver/parseable_output.swift b/test/Driver/parseable_output.swift index b955298cfd568..46458c013c404 100644 --- a/test/Driver/parseable_output.swift +++ b/test/Driver/parseable_output.swift @@ -94,7 +94,7 @@ // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output.swift.tmp.swiftsourceinfo" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "objc-header", +// CHECK-NEXT: "type": "clang-header", // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output.swift.tmp.h" // CHECK-NEXT: } // CHECK-NEXT: ], diff --git a/test/Driver/parseable_output_unicode.swift b/test/Driver/parseable_output_unicode.swift index e9065ee92be91..f8e8925cfe430 100644 --- a/test/Driver/parseable_output_unicode.swift +++ b/test/Driver/parseable_output_unicode.swift @@ -96,7 +96,7 @@ // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output_unicode.swift.tmp.swiftsourceinfo" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "objc-header", +// CHECK-NEXT: "type": "clang-header", // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output_unicode.swift.tmp.h" // CHECK-NEXT: } // CHECK-NEXT: ], diff --git a/test/Frontend/supplementary-output-support.swift b/test/Frontend/supplementary-output-support.swift index fc34e980ef885..30fa6bf7406ef 100644 --- a/test/Frontend/supplementary-output-support.swift +++ b/test/Frontend/supplementary-output-support.swift @@ -18,18 +18,11 @@ // RESOLVE_IMPORTS_NO_REFERENCE_DEPS: error: this mode does not support emitting reference dependency files{{$}} // RUN: not %target-swift-frontend -parse -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_OBJC_HEADER %s -// PARSE_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}} +// PARSE_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C or C++ headers{{$}} // RUN: not %target-swift-frontend -dump-ast -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=DUMP_NO_OBJC_HEADER %s -// DUMP_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}} +// DUMP_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C or C++ headers{{$}} // RUN: not %target-swift-frontend -resolve-imports -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=RESOLVE_IMPORTS_NO_OBJC_HEADER %s -// RESOLVE_IMPORTS_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}} - -// RUN: not %target-swift-frontend -parse -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_CXX_HEADER %s -// PARSE_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}} -// RUN: not %target-swift-frontend -dump-ast -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=DUMP_NO_CXX_HEADER %s -// DUMP_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}} -// RUN: not %target-swift-frontend -resolve-imports -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=RESOLVE_IMPORTS_NO_CXX_HEADER %s -// RESOLVE_IMPORTS_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}} +// RESOLVE_IMPORTS_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C or C++ headers{{$}} // RUN: not %target-swift-frontend -parse -emit-module-interface-path %t %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_INTERFACE %s // PARSE_NO_INTERFACE: error: this mode does not support emitting module interface files{{$}} diff --git a/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp b/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp index 130434cbefdd4..ecef6bad7f3a9 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp +++ b/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %S/cdecl.swift -typecheck -module-name CdeclFunctions -emit-cxx-header-path %t/functions.h +// RUN: %target-swift-frontend %S/cdecl.swift -typecheck -module-name CdeclFunctions -enable-cxx-interop -emit-clang-header-path %t/functions.h // RUN: %target-interop-build-clangxx -c %s -I %t -o %t/cdecl-execution.o // RUN: %target-interop-build-swift %S/cdecl.swift -o %t/cdecl-execution -Xlinker %t/cdecl-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain diff --git a/test/Interop/SwiftToCxx/functions/cdecl.swift b/test/Interop/SwiftToCxx/functions/cdecl.swift index 8b1c73585b9d1..94055be7bee29 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl.swift +++ b/test/Interop/SwiftToCxx/functions/cdecl.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name CdeclFunctions -emit-cxx-header-path %t/cdecl.h +// RUN: %target-swift-frontend %s -typecheck -module-name CdeclFunctions -enable-cxx-interop -emit-clang-header-path %t/cdecl.h // RUN: %FileCheck %s < %t/cdecl.h // RUN: %check-interop-cxx-header-in-clang(%t/cdecl.h) diff --git a/test/Interop/SwiftToCxx/functions/function-availability.swift b/test/Interop/SwiftToCxx/functions/function-availability.swift index 54f9dfb1d958e..b3d9f7ca116fb 100644 --- a/test/Interop/SwiftToCxx/functions/function-availability.swift +++ b/test/Interop/SwiftToCxx/functions/function-availability.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Functions -emit-cxx-header-path %t/functions.h +// RUN: %target-swift-frontend %s -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h // RUN: %FileCheck %s < %t/functions.h // RUN: %check-interop-cxx-header-in-clang(%t/functions.h) diff --git a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp index 08a7d0c5475a9..e9c834d7b87eb 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp +++ b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %S/swift-functions.swift -typecheck -module-name Functions -emit-cxx-header-path %t/functions.h +// RUN: %target-swift-frontend %S/swift-functions.swift -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h // RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-functions-execution.o // RUN: %target-interop-build-swift %S/swift-functions.swift -o %t/swift-functions-execution -Xlinker %t/swift-functions-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain diff --git a/test/Interop/SwiftToCxx/functions/swift-functions.swift b/test/Interop/SwiftToCxx/functions/swift-functions.swift index e37e24bff13ce..6b889d20959c0 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions.swift +++ b/test/Interop/SwiftToCxx/functions/swift-functions.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Functions -emit-cxx-header-path %t/functions.h +// RUN: %target-swift-frontend %s -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h // RUN: %FileCheck %s < %t/functions.h // RUN: %check-interop-cxx-header-in-clang(%t/functions.h) diff --git a/test/Interop/SwiftToCxx/module/module-to-namespace.swift b/test/Interop/SwiftToCxx/module/module-to-namespace.swift index eeba2992d63b5..db53e90cd3167 100644 --- a/test/Interop/SwiftToCxx/module/module-to-namespace.swift +++ b/test/Interop/SwiftToCxx/module/module-to-namespace.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -module-name Test -emit-cxx-header-path %t/empty.h +// RUN: %target-swift-frontend %s -typecheck -module-name Test -enable-cxx-interop -emit-clang-header-path %t/empty.h // RUN: %FileCheck %s < %t/empty.h // RUN: %check-interop-cxx-header-in-clang(%t/empty.h) diff --git a/test/PrintAsCxx/empty.swift b/test/PrintAsCxx/empty.swift index 0b3c6c64c9b07..317d3a8416f12 100644 --- a/test/PrintAsCxx/empty.swift +++ b/test/PrintAsCxx/empty.swift @@ -1,13 +1,7 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -emit-cxx-header-path %t/empty.h +// RUN: %target-swift-frontend %s -typecheck -enable-cxx-interop -emit-clang-header-path %t/empty.h // RUN: %FileCheck %s < %t/empty.h -// RUN: %check-cxx-header-in-clang -std=c++14 %t/empty.h -// RUN: %check-cxx-header-in-clang -std=c++17 %t/empty.h - -// CHECK-NOT: @import Swift; -// CHECK-NOT: IBSegueAction - // CHECK-LABEL: #ifndef EMPTY_SWIFT_H // CHECK-NEXT: #define EMPTY_SWIFT_H @@ -63,7 +57,19 @@ // CHECK: # define SWIFT_EXTENSION(M) // CHECK: # define OBJC_DESIGNATED_INITIALIZER -// CHECK-LABEL: namespace empty { -// CHECK: } // namespace empty +// CHECK-LABEL: #if defined(__OBJC__) +// CHECK-NEXT: #if !defined(IBSegueAction) +// CHECK-NEXT: # define IBSegueAction +// CHECK-NEXT: #endif + +// CHECK-LABEL: #if defined(__OBJC__) +// CHECK-NEXT: #if __has_feature(modules) + +// CHECK-LABEL: #if defined(__OBJC__) +// CHECK-NEXT: #endif +// CHECK-NEXT: #if defined(__cplusplus) +// CHECK-NEXT: namespace empty { +// CHECK: } // namespace empty +// CHECK: #endif // CHECK-NOT: @ diff --git a/test/PrintAsObjC/Inputs/comments-expected-output.h b/test/PrintAsObjC/Inputs/comments-expected-output.h index eccfc9b978141..2a23d5ed3d579 100644 --- a/test/PrintAsObjC/Inputs/comments-expected-output.h +++ b/test/PrintAsObjC/Inputs/comments-expected-output.h @@ -424,6 +424,9 @@ SWIFT_CLASS("_TtC8comments13UnorderedList") - (void)f0; @end +#endif +#if defined(__cplusplus) +#endif #if __has_attribute(external_source_symbol) # pragma clang attribute pop #endif From 2dd97748f3329ca8568b4fd713e41eb4a919ea9c Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Mar 2022 11:17:09 -0700 Subject: [PATCH 178/242] [CSGen] Use correct locator for member references in enum element patterns Referencing a member in pattern context is not a regular member reference, it should use only enum element declarations, just like leading-dot syntax does, so both locators should end with `pattern matching` element to indicate that to the member lookup. Resolves: rdar://90347159 --- lib/Sema/CSGen.cpp | 3 +-- test/expr/closure/multi_statement.swift | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index b0464b870fcf5..91ac41b583232 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -2584,8 +2584,7 @@ namespace { parentMetaType, enumPattern->getName(), memberType, CurDC, functionRefKind, {}, CS.getConstraintLocator(locator, - {LocatorPathElt::PatternMatch(pattern), - ConstraintLocator::Member})); + LocatorPathElt::PatternMatch(pattern))); // Parent type needs to be convertible to the pattern type; this // accounts for cases where the pattern type is existential. diff --git a/test/expr/closure/multi_statement.swift b/test/expr/closure/multi_statement.swift index efb2b1105d96c..54dc906e272c4 100644 --- a/test/expr/closure/multi_statement.swift +++ b/test/expr/closure/multi_statement.swift @@ -249,3 +249,26 @@ func test_taps_type_checked_with_correct_decl_context() { return false } } + +// rdar://90347159 - in pattern matching context `case` should be preferred over static declarations +func test_pattern_matches_only_cases() { + enum ParsingError : Error { + case ok(Int) + case failed([Error], Int) + + static var ok: Int { 42 } + static func failed(_: [Error], at: Any) -> Self { fatalError() } + } + + let _: (ParsingError) -> Void = { + switch $0 { + case let ParsingError.failed(errors, _): print(errors) // Ok + default: break + } + + switch $0 { + case let ParsingError.ok(result): print(result) // Ok + default: break + } + } +} From c5cef953bc85a585142edb2f35ea233b3a65ce6f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 23:11:18 -0400 Subject: [PATCH 179/242] RequirementMachine: Minimal conformances collects protocol refinement rules from all minimization domains --- .../MinimalConformances.cpp | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/AST/RequirementMachine/MinimalConformances.cpp b/lib/AST/RequirementMachine/MinimalConformances.cpp index bc5121af4d0d8..018b261a41b76 100644 --- a/lib/AST/RequirementMachine/MinimalConformances.cpp +++ b/lib/AST/RequirementMachine/MinimalConformances.cpp @@ -159,16 +159,18 @@ class MinimalConformances { DebugOptions Debug; - // All conformance rules, sorted by (isExplicit(), getLHS()), with non-explicit - // rules with longer left hand sides coming first. + // All conformance rules in the current minimization domain, sorted by + // (isExplicit(), getLHS()), with non-explicit rules with longer left hand + // sides coming first. // // The idea here is that we want less canonical rules to be eliminated first, // but we prefer to eliminate non-explicit rules, in an attempt to keep protocol // conformance rules in the same protocol as they were originally defined in. SmallVector ConformanceRules; - // Maps a conformance rule to a conformance path deriving the subject type's - // base type. For example, consider the following conformance rule: + // Maps a conformance rule in the current minimization domain to a conformance + // path deriving the subject type's base type. For example, consider the + // following conformance rule: // // T.[P:A].[Q:B].[R] => T.[P:A].[Q:B] // @@ -177,15 +179,17 @@ class MinimalConformances { // path for the term T.[P:A].[Q], known as the 'parent path'. llvm::MapVector> ParentPaths; - // Maps a conformance rule to a list of paths. Each path in the list is a unique - // derivation of the conformance in terms of other conformance rules. + // Maps a conformance rule in the current minimization domain to a list of paths. + // Each path in the list is a unique derivation of the conformance in terms of + // other conformance rules. llvm::MapVector>> ConformancePaths; - // The set of conformance rules which are protocol refinements, that is rules of - // the form [P].[Q] => [P]. + // The set of conformance rules (from all minimization domains) which are protocol + // refinements, that is rules of the form [P].[Q] => [P]. llvm::DenseSet ProtocolRefinements; - // This is the result. + // This is the computed result set of redundant conformance rules in the current + // minimization domain. llvm::DenseSet &RedundantConformances; void decomposeTermIntoConformanceRuleLeftHandSides( @@ -391,17 +395,15 @@ void MinimalConformances::collectConformanceRules() { if (!rule.isAnyConformanceRule()) continue; + // Save protocol refinement relations in a side table. + if (rule.isProtocolRefinementRule()) + ProtocolRefinements.insert(ruleID); + if (!System.isInMinimizationDomain(rule.getLHS().getRootProtocol())) continue; ConformanceRules.push_back(ruleID); - // Save protocol refinement relations in a side table. - if (rule.isProtocolRefinementRule()) { - ProtocolRefinements.insert(ruleID); - continue; - } - auto lhs = rule.getLHS(); // Record a parent path if the subject type itself requires a non-trivial @@ -698,7 +700,7 @@ bool MinimalConformances::isValidConformancePath( bool MinimalConformances::isValidRefinementPath( const llvm::SmallVectorImpl &path) const { for (unsigned ruleID : path) { - if (!System.getRule(ruleID).isProtocolRefinementRule()) + if (ProtocolRefinements.count(ruleID) == 0) return false; } @@ -867,8 +869,14 @@ void MinimalConformances::computeMinimalConformances() { for (const auto &path : paths) { // Only consider a protocol refinement rule to be redundant if it is // witnessed by a composition of other protocol refinement rules. - if (isProtocolRefinement && !isValidRefinementPath(path)) + if (isProtocolRefinement && !isValidRefinementPath(path)) { + if (Debug.contains(DebugFlags::MinimalConformances)) { + llvm::dbgs() << "Not a refinement path: "; + dumpConformancePath(llvm::errs(), path); + llvm::dbgs() << "\n"; + } continue; + } llvm::SmallDenseSet visited; visited.insert(ruleID); From 30da30e0ee6ff3cab2c37a7188af6afeb682c2dd Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 23:11:41 -0400 Subject: [PATCH 180/242] RequirementMachine: Rule::isProtocolRefinementRule() allows transitive refinement Without this, we only considered a protocol refinement rule redundant if it was derived by directly-stated protocol refinements. But this broke when completion introduced a 'transitive' refinement [P].[R] => [P] from two directly-stated refinements [P].[Q] => [P] and [Q].[R] => [Q]. Fixes rdar://problem/90402503. --- .../RequirementMachine/MinimalConformances.cpp | 2 +- lib/AST/RequirementMachine/Rule.cpp | 4 ++-- lib/AST/RequirementMachine/Rule.h | 2 +- .../redundant_protocol_refinement.swift | 17 ++++++++++++----- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/AST/RequirementMachine/MinimalConformances.cpp b/lib/AST/RequirementMachine/MinimalConformances.cpp index 018b261a41b76..fa6a0740b3708 100644 --- a/lib/AST/RequirementMachine/MinimalConformances.cpp +++ b/lib/AST/RequirementMachine/MinimalConformances.cpp @@ -396,7 +396,7 @@ void MinimalConformances::collectConformanceRules() { continue; // Save protocol refinement relations in a side table. - if (rule.isProtocolRefinementRule()) + if (rule.isProtocolRefinementRule(Context)) ProtocolRefinements.insert(ruleID); if (!System.isInMinimizationDomain(rule.getLHS().getRootProtocol())) diff --git a/lib/AST/RequirementMachine/Rule.cpp b/lib/AST/RequirementMachine/Rule.cpp index e7dd0e7b89aba..a89ccac6ab0d1 100644 --- a/lib/AST/RequirementMachine/Rule.cpp +++ b/lib/AST/RequirementMachine/Rule.cpp @@ -93,7 +93,7 @@ bool Rule::isIdentityConformanceRule() const { /// If this is a rule of the form [P].[Q] => [P] where [P] and [Q] are /// protocol symbols, return true, otherwise return false. -bool Rule::isProtocolRefinementRule() const { +bool Rule::isProtocolRefinementRule(RewriteContext &ctx) const { if (LHS.size() == 2 && RHS.size() == 1 && LHS[0] == RHS[0] && @@ -111,7 +111,7 @@ bool Rule::isProtocolRefinementRule() const { auto *proto = LHS[0].getProtocol(); auto *otherProto = LHS[1].getProtocol(); - auto inherited = proto->getInheritedProtocols(); + auto inherited = ctx.getInheritedProtocols(proto); return (std::find(inherited.begin(), inherited.end(), otherProto) != inherited.end()); } diff --git a/lib/AST/RequirementMachine/Rule.h b/lib/AST/RequirementMachine/Rule.h index f4c623b2d0b16..e650547e93c02 100644 --- a/lib/AST/RequirementMachine/Rule.h +++ b/lib/AST/RequirementMachine/Rule.h @@ -108,7 +108,7 @@ class Rule final { bool isIdentityConformanceRule() const; - bool isProtocolRefinementRule() const; + bool isProtocolRefinementRule(RewriteContext &ctx) const; bool isCircularConformanceRule() const; diff --git a/test/Generics/redundant_protocol_refinement.swift b/test/Generics/redundant_protocol_refinement.swift index 70af031c12929..8459164f9501d 100644 --- a/test/Generics/redundant_protocol_refinement.swift +++ b/test/Generics/redundant_protocol_refinement.swift @@ -1,22 +1,29 @@ // RUN: %target-typecheck-verify-swift // RUN: %target-swift-frontend -typecheck -debug-generic-signatures -requirement-machine-protocol-signatures=verify %s 2>&1 | %FileCheck %s -protocol Base {} - // CHECK-LABEL: redundant_protocol_refinement.(file).Base@ // CHECK-LABEL: Requirement signature: - -protocol Middle : Base {} +protocol Base {} // CHECK-LABEL: redundant_protocol_refinement.(file).Middle@ // CHECK-LABEL: Requirement signature: +protocol Middle : Base {} +// CHECK-LABEL: redundant_protocol_refinement.(file).Derived@ +// CHECK-LABEL: Requirement signature: protocol Derived : Middle, Base {} // expected-note@-1 {{conformance constraint 'Self' : 'Base' implied here}} // expected-warning@-2 {{redundant conformance constraint 'Self' : 'Base'}} -// CHECK-LABEL: redundant_protocol_refinement.(file).Derived@ +// CHECK-LABEL: redundant_protocol_refinement.(file).Derived2@ // CHECK-LABEL: Requirement signature: +protocol Derived2 : Middle {} + +// CHECK-LABEL: redundant_protocol_refinement.(file).MoreDerived@ +// CHECK-LABEL: Requirement signature: +protocol MoreDerived : Derived2, Base {} +// expected-note@-1 {{conformance constraint 'Self' : 'Base' implied here}} +// expected-warning@-2 {{redundant conformance constraint 'Self' : 'Base'}} protocol P1 {} From 979196cb16a7380cac60dadae1a4c4cd4d7117ea Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Thu, 17 Mar 2022 12:06:53 -0700 Subject: [PATCH 181/242] [Test] For disabling destroy hoisting. --- test/SILOptimizer/assemblyvision_remark/chacha.swift | 5 +++-- test/SILOptimizer/enum_payload_modification_opt.swift | 1 + test/SILOptimizer/pointer_conversion.swift | 4 ---- test/SILOptimizer/templvalueopt.swift | 2 +- validation-test/SILOptimizer/lexical-lifetimes.swift | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/test/SILOptimizer/assemblyvision_remark/chacha.swift b/test/SILOptimizer/assemblyvision_remark/chacha.swift index 1398e2de1813d..84f2317754609 100644 --- a/test/SILOptimizer/assemblyvision_remark/chacha.swift +++ b/test/SILOptimizer/assemblyvision_remark/chacha.swift @@ -32,8 +32,8 @@ public func run_ChaCha(_ N: Int) { var checkedtext = Array(repeating: UInt8(0), count: 1024) ChaCha20.encrypt(bytes: &checkedtext, key: key, nonce: nonce) - checkResult(checkedtext) // expected-remark {{release of type '}} - // expected-note @-3 {{of 'checkedtext}} + checkResult(checkedtext)// expected-note @-2 {{of 'checkedtext}} + var plaintext = Array(repeating: UInt8(0), count: 30720) for _ in 1...N { @@ -47,3 +47,4 @@ public func run_ChaCha(_ N: Int) { // expected-note @-16 {{of 'nonce}} // expected-remark @-4 {{release of type '}} // expected-note @-19 {{of 'key}} + // expected-remark @-6 {{release of type '}} diff --git a/test/SILOptimizer/enum_payload_modification_opt.swift b/test/SILOptimizer/enum_payload_modification_opt.swift index 52cf7038237b6..fdb3a500c5ad4 100644 --- a/test/SILOptimizer/enum_payload_modification_opt.swift +++ b/test/SILOptimizer/enum_payload_modification_opt.swift @@ -3,6 +3,7 @@ // RUN: %target-run %t/a.out | %FileCheck %s // REQUIRES: executable_test,swift_stdlib_no_asserts,optimized_stdlib +// REQUIRES: rdar89260664 final class Storage { var v : Int diff --git a/test/SILOptimizer/pointer_conversion.swift b/test/SILOptimizer/pointer_conversion.swift index ca5582c74a6c5..d4c25e9e5b7a8 100644 --- a/test/SILOptimizer/pointer_conversion.swift +++ b/test/SILOptimizer/pointer_conversion.swift @@ -58,11 +58,9 @@ public func testMutableArray() { // CHECK: [[POINTER:%.+]] = struct $UnsafeMutableRawPointer ( // CHECK-NEXT: [[DEP_POINTER:%.+]] = mark_dependence [[POINTER]] : $UnsafeMutableRawPointer on {{.*}} : $__ContiguousArrayStorageBase // CHECK: [[FN:%.+]] = function_ref @takesMutableRawPointer - // CHECK: strong_retain {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} // CHECK: apply [[FN]]([[DEP_POINTER]]) // CHECK-NOT: {{^bb[0-9]+:}} // CHECK: strong_release {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} - // CHECK: strong_release {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} // CHECK: dealloc_stack {{%.+}} : $*Array // CHECK-NEXT: [[EMPTY:%.+]] = tuple () // CHECK-NEXT: return [[EMPTY]] @@ -76,11 +74,9 @@ public func testMutableArrayToOptional() { // CHECK-NEXT: [[DEP_POINTER:%.+]] = mark_dependence [[POINTER]] : $UnsafeMutableRawPointer on {{.*}} : $__ContiguousArrayStorageBase // CHECK-NEXT: [[OPT_POINTER:%.+]] = enum $Optional, #Optional.some!enumelt, [[DEP_POINTER]] // CHECK: [[FN:%.+]] = function_ref @takesOptMutableRawPointer - // CHECK: strong_retain {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} // CHECK: apply [[FN]]([[OPT_POINTER]]) // CHECK-NOT: {{^bb[0-9]+:}} // CHECK: strong_release {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} - // CHECK: strong_release {{%.+}} : ${{Builtin[.]BridgeObject|__ContiguousArrayStorageBase}} // CHECK: dealloc_stack {{%.+}} : $*Array // CHECK-NEXT: [[EMPTY:%.+]] = tuple () // CHECK-NEXT: return [[EMPTY]] diff --git a/test/SILOptimizer/templvalueopt.swift b/test/SILOptimizer/templvalueopt.swift index 05ab18c00bfc5..0cc9d9267dd69 100644 --- a/test/SILOptimizer/templvalueopt.swift +++ b/test/SILOptimizer/templvalueopt.swift @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -module-name=test -O -emit-sil %s | %FileCheck %s +// RUN: %target-swift-frontend -module-name=test -O -enable-ossa-modules -emit-sil %s | %FileCheck %s // RUN: %empty-directory(%t) // RUN: %target-build-swift -O -module-name=test %s -o %t/a.out diff --git a/validation-test/SILOptimizer/lexical-lifetimes.swift b/validation-test/SILOptimizer/lexical-lifetimes.swift index e6afe429100ef..27f9d3b5f01c2 100644 --- a/validation-test/SILOptimizer/lexical-lifetimes.swift +++ b/validation-test/SILOptimizer/lexical-lifetimes.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library -Xfrontend -enable-copy-propagation -Xllvm -enable-destroy-hoisting=false) | %FileCheck %s +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -parse-as-library -Xfrontend -enable-copy-propagation) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency From 064bce49f6710eebb2aeb5f6e0a9938d325201e0 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 17 Mar 2022 14:08:15 -0400 Subject: [PATCH 182/242] Add regression test for https://bugs.swift.org/browse/SR-15979 --- test/type/parameterized_protocol.swift | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/type/parameterized_protocol.swift b/test/type/parameterized_protocol.swift index 8e270633d54a3..18ec8eae670c5 100644 --- a/test/type/parameterized_protocol.swift +++ b/test/type/parameterized_protocol.swift @@ -1,6 +1,6 @@ -// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=verify -requirement-machine-inferred-signatures=verify -enable-parameterized-protocol-types -disable-availability-checking +// RUN: %target-typecheck-verify-swift -requirement-machine-protocol-signatures=on -requirement-machine-inferred-signatures=on -enable-parameterized-protocol-types -disable-availability-checking -// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=verify -enable-parameterized-protocol-types -requirement-machine-inferred-signatures=verify -disable-availability-checking 2>&1 | %FileCheck %s +// RUN: not %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on -enable-parameterized-protocol-types -requirement-machine-inferred-signatures=on -disable-availability-checking 2>&1 | %FileCheck %s /// Test some invalid syntax first @@ -77,6 +77,13 @@ protocol SequenceWrapperProtocol { associatedtype E } +// https://bugs.swift.org/browse/SR-15979 - the GenericSignatureBuilder doesn't like this protocol. + +// CHECK-LABEL: .Recursive@ +// CHECK-NEXT: Requirement signature: +protocol Recursive { + associatedtype D: Recursive = Self +} /// Parametrized protocol in where clause of concrete type @@ -179,6 +186,8 @@ protocol SomeProto {} func protocolCompositionNotSupported(_: SomeProto & Sequence) {} // expected-error@-1 {{non-protocol, non-class type 'Sequence' cannot be used within a protocol-constrained type}} +/// More regression tests + protocol DoubleWide { var x: X { get } var y: Y { get } From 10324641f94addd305336777e5b711039f475990 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Mar 2022 12:10:28 -0700 Subject: [PATCH 183/242] [TypeChecker] NFC: Restrict multi-statement closure + simd test-case to macOS Resolves: rdar://90445018 --- .../fast/multi_statement_closure_with_simd_variable.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift b/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift index 294ff03cb814c..5b24252624e7f 100644 --- a/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift +++ b/validation-test/Sema/type_checker_perf/fast/multi_statement_closure_with_simd_variable.swift @@ -1,5 +1,6 @@ // RUN: %target-typecheck-verify-swift -solver-expression-time-threshold=1 // REQUIRES: objc_interop,no_asan +// REQUIRES: OS=macosx import simd From 613fdca3a8b1bec181971ded84ba86a27522ad5d Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Mar 2022 12:00:50 -0700 Subject: [PATCH 184/242] [CSDiagnostics] Fix trailing closure ambiguity note to not expect that anchor is expression `TrailingClosureAmbiguityFailure::diagnoseAsNote()` is used by `diagnoseAmbiguity` opportunistically, which means that anchor could be a pattern or a statement condition element. --- lib/Sema/CSDiagnostics.cpp | 7 ++++++- test/expr/closure/multi_statement.swift | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index faf8d8bbd45be..80de3fe163dcb 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -1746,7 +1746,12 @@ bool TypeChecker::diagnoseSelfAssignment(const Expr *expr) { } bool TrailingClosureAmbiguityFailure::diagnoseAsNote() { - auto *anchor = castToExpr(getAnchor()); + auto *anchor = getAsExpr(getAnchor()); + // This diagnostic is used opportunistically in `diagnoseAmbiguity`, + // which means it cannot assume that anchor is always an expression. + if (!anchor) + return false; + const auto *expr = findParentExpr(anchor); auto *callExpr = dyn_cast_or_null(expr); if (!callExpr) diff --git a/test/expr/closure/multi_statement.swift b/test/expr/closure/multi_statement.swift index efb2b1105d96c..e1662b46ad68d 100644 --- a/test/expr/closure/multi_statement.swift +++ b/test/expr/closure/multi_statement.swift @@ -234,6 +234,20 @@ func test_local_function_capturing_vars() { } } +func test_pattern_ambiguity_doesnot_crash_compiler() { + enum E { + case hello(result: Int) // expected-note {{found this candidate}} + case hello(status: Int) // expected-note {{found this candidate}} + } + + let _: (E) -> Void = { + switch $0 { + case let E.hello(x): print(x) // expected-error {{ambiguous use of 'hello'}} + default: break + } + } +} + func test_taps_type_checked_with_correct_decl_context() { struct Path { func contains(_: T) -> Bool where T: StringProtocol { return false } From ebdef12fe13a828b9042789e460808d910444941 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Mar 2022 13:01:59 -0700 Subject: [PATCH 185/242] [ContraintSystem] Augment `diagnoseAmbiguity` to diagnose non-expression ambiguities Since constraint solver can now handle statements, patterns, and declarations, it's possible to that ambiguity could be detected in a non-expression context, for example - pattern matching in switch statements. Augment `diagnoseAmbiguity` to accept overloads with non-expression anchors and diagnose them in order of their appearance in a solution. --- lib/Sema/ConstraintSystem.cpp | 32 +++++++++++++++---------- test/expr/closure/multi_statement.swift | 10 ++++++-- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index be39c170c6772..bb1d6093e47ff 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -4501,7 +4501,7 @@ bool ConstraintSystem::diagnoseAmbiguity(ArrayRef solutions) { auto &overload = diff.overloads[i]; auto *locator = overload.locator; - Expr *anchor = nullptr; + ASTNode anchor; // Simplification of member locator would produce a base expression, // this is what we want for diagnostics but not for comparisons here @@ -4511,25 +4511,33 @@ bool ConstraintSystem::diagnoseAmbiguity(ArrayRef solutions) { // than simplification of `count` would produce `[x]` which is incorrect. if (locator->isLastElement() || locator->isLastElement()) { - anchor = getAsExpr(locator->getAnchor()); + anchor = locator->getAnchor(); } else { - anchor = getAsExpr(simplifyLocatorToAnchor(overload.locator)); + anchor = simplifyLocatorToAnchor(overload.locator); } - // If we can't resolve the locator to an anchor expression with no path, + // If we can't resolve the locator to an anchor with no path, // we can't diagnose this well. if (!anchor) continue; - auto it = indexMap.find(anchor); - if (it == indexMap.end()) - continue; - unsigned index = it->second; + // Index and Depth is only applicable to expressions. + unsigned index = 0; + unsigned depth = 0; - auto optDepth = getExprDepth(anchor); - if (!optDepth) - continue; - unsigned depth = *optDepth; + if (auto *expr = getAsExpr(anchor)) { + auto it = indexMap.find(expr); + if (it == indexMap.end()) + continue; + + index = it->second; + + auto optDepth = getExprDepth(expr); + if (!optDepth) + continue; + + depth = *optDepth; + } // If we don't have a name to hang on to, it'll be hard to diagnose this // overload. diff --git a/test/expr/closure/multi_statement.swift b/test/expr/closure/multi_statement.swift index e1662b46ad68d..392f072a416fc 100644 --- a/test/expr/closure/multi_statement.swift +++ b/test/expr/closure/multi_statement.swift @@ -236,8 +236,14 @@ func test_local_function_capturing_vars() { func test_pattern_ambiguity_doesnot_crash_compiler() { enum E { - case hello(result: Int) // expected-note {{found this candidate}} - case hello(status: Int) // expected-note {{found this candidate}} + case hello(result: Int) // expected-note 2 {{found this candidate}} + case hello(status: Int) // expected-note 2 {{found this candidate}} + } + + let _: (E) -> Void = { + switch $0 { + case .hello(_): break // expected-error {{ambiguous use of 'hello'}} + } } let _: (E) -> Void = { From 41f176b7bf684a9da0681208b0d4300e170689cc Mon Sep 17 00:00:00 2001 From: Ben Barham Date: Thu, 17 Mar 2022 13:12:10 -0700 Subject: [PATCH 186/242] [Serialization] Abort if serializing CustomAttr with no type Rather than crash when deserializing a CustomAttr with no type, crash when serializing it instead (which is usually easier to diagnose). --- lib/Serialization/Serialization.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index fb3eb39486904..c51f42a27d062 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2703,10 +2703,14 @@ class Serializer::DeclSerializer : public DeclVisitor { case DAK_Custom: { auto abbrCode = S.DeclTypeAbbrCodes[CustomDeclAttrLayout::Code]; auto theAttr = cast(DA); + auto typeID = S.addTypeRef(theAttr->getType()); + if (!typeID && !S.allowCompilerErrors()) { + llvm::PrettyStackTraceString message("CustomAttr has no type"); + abort(); + } CustomDeclAttrLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode, theAttr->isImplicit(), - S.addTypeRef(theAttr->getType()), - theAttr->isArgUnsafe()); + typeID, theAttr->isArgUnsafe()); return; } From 7e182cf43d8877deea5531960d3d8bff97c82c82 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Mar 2022 14:00:39 -0700 Subject: [PATCH 187/242] [ASTMangler] Add a special entry point for mangling of distributed thunks Distributed thunks loose all of the marker protocols associated with their generic parameters. --- include/swift/AST/ASTMangler.h | 2 ++ lib/AST/ASTMangler.cpp | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index f0fe872729d68..038d13fa67f4b 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -186,6 +186,8 @@ class ASTMangler : public Mangler { Type GlobalActorBound, ModuleDecl *Module); + std::string mangleDistributedThunk(const FuncDecl *thunk); + /// Mangle a completion handler block implementation function, used for importing ObjC /// APIs as async. /// diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index cf293da67ae12..b6a967522ae54 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -3457,3 +3457,11 @@ ASTMangler::mangleOpaqueTypeDescriptorRecord(const OpaqueTypeDecl *decl) { appendOperator("Ho"); return finalize(); } + +std::string ASTMangler::mangleDistributedThunk(const FuncDecl *thunk) { + // Marker protocols cannot be checked at runtime, so there is no point + // in recording them for distributed thunks. + llvm::SaveAndRestore savedAllowMarkerProtocols(AllowMarkerProtocols, + false); + return mangleEntity(thunk, SymbolKind::DistributedThunk); +} From c9a0295d6d34d2fed8f02b61dab9fa94e40f907a Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Mar 2022 14:02:42 -0700 Subject: [PATCH 188/242] [Distributed] Synthesis: Start using special mangling for distributed thunks --- lib/SIL/IR/SILDeclRef.cpp | 4 ++++ lib/Sema/CodeSynthesisDistributedActor.cpp | 9 ++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/SIL/IR/SILDeclRef.cpp b/lib/SIL/IR/SILDeclRef.cpp index 174f909c35419..ea019127664a2 100644 --- a/lib/SIL/IR/SILDeclRef.cpp +++ b/lib/SIL/IR/SILDeclRef.cpp @@ -914,6 +914,10 @@ std::string SILDeclRef::mangle(ManglingKind MKind) const { return CDeclA->Name.str(); } + if (SKind == ASTMangler::SymbolKind::DistributedThunk) { + return mangler.mangleDistributedThunk(cast(getDecl())); + } + // Otherwise, fall through into the 'other decl' case. LLVM_FALLTHROUGH; diff --git a/lib/Sema/CodeSynthesisDistributedActor.cpp b/lib/Sema/CodeSynthesisDistributedActor.cpp index 3516f0108a6e4..72ba5166fcffa 100644 --- a/lib/Sema/CodeSynthesisDistributedActor.cpp +++ b/lib/Sema/CodeSynthesisDistributedActor.cpp @@ -375,11 +375,10 @@ deriveBodyDistributed_thunk(AbstractFunctionDecl *thunk, void *context) { { // --- Mangle the thunk name Mangle::ASTMangler mangler; - auto symbolKind = swift::Mangle::ASTMangler::SymbolKind::DistributedThunk; - auto mangled = C.AllocateCopy(mangler.mangleEntity(thunk, symbolKind)); - StringRef mangledTargetStringRef = StringRef(mangled); - auto mangledTargetStringLiteral = new (C) - StringLiteralExpr(mangledTargetStringRef, SourceRange(), implicit); + auto mangled = + C.AllocateCopy(mangler.mangleDistributedThunk(cast(thunk))); + auto mangledTargetStringLiteral = + new (C) StringLiteralExpr(mangled, SourceRange(), implicit); // --- let target = RemoteCallTarget() targetVar->setInterfaceType(remoteCallTargetTy); From eaf3f316ec54084bb48fbf245edef60b1b9d3542 Mon Sep 17 00:00:00 2001 From: David Smith Date: Thu, 17 Mar 2022 14:18:21 -0700 Subject: [PATCH 189/242] Vectorize UTF16 offset calculations --- stdlib/public/core/StringUTF16View.swift | 109 +++++++++++++++++- .../stdlib/StringBreadcrumbs.swift | 2 - 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/stdlib/public/core/StringUTF16View.swift b/stdlib/public/core/StringUTF16View.swift index 9677a70256678..aac81751ab50f 100644 --- a/stdlib/public/core/StringUTF16View.swift +++ b/stdlib/public/core/StringUTF16View.swift @@ -137,7 +137,7 @@ extension String.UTF16View: BidirectionalCollection { /// In an empty UTF-16 view, `endIndex` is equal to `startIndex`. @inlinable @inline(__always) public var endIndex: Index { return _guts.endIndex } - + @inlinable @inline(__always) public func index(after idx: Index) -> Index { if _slowPath(_guts.isForeign) { return _foreignIndex(after: idx) } @@ -149,6 +149,7 @@ extension String.UTF16View: BidirectionalCollection { // TODO: If transcoded is 1, can we just skip ahead 4? let idx = _utf16AlignNativeIndex(idx) + let len = _guts.fastUTF8ScalarLength(startingAt: idx._encodedOffset) if len == 4 && idx.transcodedOffset == 0 { return idx.nextTranscoded @@ -518,6 +519,105 @@ extension _StringGuts { } extension String.UTF16View { + + @inline(__always) + internal func _utf16Length( + readPtr: inout UnsafeRawPointer, + endPtr: UnsafeRawPointer, + unsignedSIMDType: U.Type, + signedSIMDType: S.Type + ) -> Int where U.Scalar == UInt8, S.Scalar == Int8 { + var utf16Count = 0 + + while readPtr + MemoryLayout.stride < endPtr { + //Find the number of continuations (0b10xxxxxx) + let sValue = Builtin.loadRaw(readPtr._rawValue) as S + let continuations = S.zero.replacing(with: S.one, where: sValue .< -65 + 1) + let continuationCount = Int(continuations.wrappedSum()) + + //Find the number of 4 byte code points (0b11110xxx) + let uValue = Builtin.loadRaw(readPtr._rawValue) as U + let fourBytes = U.zero.replacing(with: U.one, where: uValue .>= 0b11110000) + let fourByteCount = Int(fourBytes.wrappedSum()) + + utf16Count &+= (U.scalarCount - continuationCount) + fourByteCount + + readPtr += MemoryLayout.stride + } + + return utf16Count + } + + @inline(__always) + internal func _utf16Distance(from start: Index, to end: Index) -> Int { + _internalInvariant(end.transcodedOffset == 0 || end.transcodedOffset == 1) + + return (end.transcodedOffset - start.transcodedOffset) + _guts.withFastUTF8( + range: start._encodedOffset ..< end._encodedOffset + ) { utf8 in + let rawBuffer = UnsafeRawBufferPointer(utf8) + guard rawBuffer.count > 0 else { return 0 } + + var utf16Count = 0 + var readPtr = rawBuffer.baseAddress.unsafelyUnwrapped + let initialReadPtr = readPtr + let endPtr = readPtr + rawBuffer.count + + //eat leading continuations + while readPtr < endPtr { + let byte = readPtr.load(as: UInt8.self) + if !UTF8.isContinuation(byte) { + break + } + readPtr += 1 + } + + // TODO: Currently, using SIMD sizes above SIMD8 is slower + // Once that's fixed we should go up to SIMD64 here + + utf16Count &+= _utf16Length( + readPtr: &readPtr, + endPtr: endPtr, + unsignedSIMDType: SIMD8.self, + signedSIMDType: SIMD8.self + ) + + //TO CONSIDER: SIMD widths <8 here + + //back up to the start of the current scalar if we may have a trailing + //incomplete scalar + if utf16Count > 0 && UTF8.isContinuation(readPtr.load(as: UInt8.self)) { + while readPtr > initialReadPtr && UTF8.isContinuation(readPtr.load(as: UInt8.self)) { + readPtr -= 1 + } + + //The trailing scalar may be incomplete, subtract it out and check below + let byte = readPtr.load(as: UInt8.self) + let len = _utf8ScalarLength(byte) + utf16Count &-= len == 4 ? 2 : 1 + if readPtr == initialReadPtr { + //if we backed up all the way and didn't hit a non-continuation, then + //we don't have any complete scalars, and we should bail. + return 0 + } + } + + //trailing bytes + while readPtr < endPtr { + let byte = readPtr.load(as: UInt8.self) + let len = _utf8ScalarLength(byte) + // if we don't have enough bytes left, we don't have a complete scalar, + // so don't add it to the count. + if readPtr + len <= endPtr { + utf16Count &+= len == 4 ? 2 : 1 + } + readPtr += len + } + + return utf16Count + } + } + @usableFromInline @_effects(releasenone) internal func _nativeGetOffset(for idx: Index) -> Int { @@ -532,9 +632,7 @@ extension String.UTF16View { let idx = _utf16AlignNativeIndex(idx) guard _guts._useBreadcrumbs(forEncodedOffset: idx._encodedOffset) else { - // TODO: Generic _distance is still very slow. We should be able to - // skip over ASCII substrings quickly - return _distance(from: startIndex, to: idx) + return _utf16Distance(from: startIndex, to: idx) } // Simple and common: endIndex aka `length`. @@ -544,7 +642,8 @@ extension String.UTF16View { // Otherwise, find the nearest lower-bound breadcrumb and count from there let (crumb, crumbOffset) = breadcrumbsPtr.pointee.getBreadcrumb( forIndex: idx) - return crumbOffset + _distance(from: crumb, to: idx) + + return crumbOffset + _utf16Distance(from: crumb, to: idx) } @usableFromInline diff --git a/validation-test/stdlib/StringBreadcrumbs.swift b/validation-test/stdlib/StringBreadcrumbs.swift index b555d39c9822c..c8f8418bf9023 100644 --- a/validation-test/stdlib/StringBreadcrumbs.swift +++ b/validation-test/stdlib/StringBreadcrumbs.swift @@ -1,5 +1,3 @@ -// rdar://84233775 -// REQUIRES: rdar84233775 // RUN: %target-run-stdlib-swift // REQUIRES: executable_test,optimized_stdlib From b0a4efba54717d3719631d540e24a83122256411 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 16:41:39 -0400 Subject: [PATCH 190/242] RequirementMachine: Fix lookupConcreteNestedType() to cope with opaque archetypes --- .../ConcreteContraction.cpp | 14 +++---------- .../GenericSignatureQueries.cpp | 4 ++-- lib/AST/RequirementMachine/NameLookup.cpp | 20 +++++++++++++++++++ lib/AST/RequirementMachine/NameLookup.h | 6 ++++++ 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/lib/AST/RequirementMachine/ConcreteContraction.cpp b/lib/AST/RequirementMachine/ConcreteContraction.cpp index a90bb78cd48e0..0ce15268953d8 100644 --- a/lib/AST/RequirementMachine/ConcreteContraction.cpp +++ b/lib/AST/RequirementMachine/ConcreteContraction.cpp @@ -235,19 +235,10 @@ Optional ConcreteContraction::substTypeParameter( ->substBaseType(module, *substBaseType); } - auto *decl = (*substBaseType)->getAnyNominal(); - if (decl == nullptr) { - if (Debug) { - llvm::dbgs() << "@@@ Not a nominal type: " << *substBaseType << "\n"; - } - - return None; - } - // An unresolved DependentMemberType stores an identifier. Handle this // by performing a name lookup into the base type. SmallVector concreteDecls; - lookupConcreteNestedType(decl, memberType->getName(), concreteDecls); + lookupConcreteNestedType(*substBaseType, memberType->getName(), concreteDecls); auto *typeDecl = findBestConcreteNestedType(concreteDecls); if (typeDecl == nullptr) { @@ -261,8 +252,9 @@ Optional ConcreteContraction::substTypeParameter( } // Substitute the base type into the member type. + auto *dc = typeDecl->getDeclContext(); auto subMap = (*substBaseType)->getContextSubstitutionMap( - decl->getParentModule(), typeDecl->getDeclContext()); + dc->getParentModule(), dc); return typeDecl->getDeclaredInterfaceType().subst(subMap); } diff --git a/lib/AST/RequirementMachine/GenericSignatureQueries.cpp b/lib/AST/RequirementMachine/GenericSignatureQueries.cpp index 18a56f95f373d..c7f113ea14ffb 100644 --- a/lib/AST/RequirementMachine/GenericSignatureQueries.cpp +++ b/lib/AST/RequirementMachine/GenericSignatureQueries.cpp @@ -17,6 +17,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/Decl.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/GenericSignature.h" #include "swift/AST/Module.h" #include @@ -659,8 +660,7 @@ RequirementMachine::lookupNestedType(Type depType, Identifier name) const { typeToSearch = props->getSuperclassBound(); if (typeToSearch) - if (auto *decl = typeToSearch->getAnyNominal()) - lookupConcreteNestedType(decl, name, concreteDecls); + lookupConcreteNestedType(typeToSearch, name, concreteDecls); } if (bestAssocType) { diff --git a/lib/AST/RequirementMachine/NameLookup.cpp b/lib/AST/RequirementMachine/NameLookup.cpp index d4f75c62a9289..0f8382e4ffd3a 100644 --- a/lib/AST/RequirementMachine/NameLookup.cpp +++ b/lib/AST/RequirementMachine/NameLookup.cpp @@ -12,13 +12,33 @@ #include "NameLookup.h" #include "swift/AST/Decl.h" +#include "swift/AST/GenericEnvironment.h" #include "swift/AST/Module.h" +#include "swift/AST/Types.h" #include "llvm/ADT/SmallVector.h" #include using namespace swift; using namespace rewriting; +void +swift::rewriting::lookupConcreteNestedType( + Type baseType, + Identifier name, + SmallVectorImpl &concreteDecls) { + if (auto *decl = baseType->getAnyNominal()) + lookupConcreteNestedType(decl, name, concreteDecls); + else if (auto *archetype = baseType->getAs()) { + // If our concrete type is an opaque result archetype, look into its + // generic environment recursively. + auto *genericEnv = archetype->getGenericEnvironment(); + auto genericSig = genericEnv->getGenericSignature(); + + concreteDecls.push_back( + genericSig->lookupNestedType(archetype->getInterfaceType(), name)); + } +} + void swift::rewriting::lookupConcreteNestedType( NominalTypeDecl *decl, diff --git a/lib/AST/RequirementMachine/NameLookup.h b/lib/AST/RequirementMachine/NameLookup.h index d44e501aba3a0..55d69683ae550 100644 --- a/lib/AST/RequirementMachine/NameLookup.h +++ b/lib/AST/RequirementMachine/NameLookup.h @@ -19,10 +19,16 @@ namespace swift { class Identifier; class NominalTypeDecl; +class Type; class TypeDecl; namespace rewriting { +void lookupConcreteNestedType( + Type baseType, + Identifier name, + llvm::SmallVectorImpl &concreteDecls); + void lookupConcreteNestedType( NominalTypeDecl *decl, Identifier name, From 1e731d3a1808b60ed4d180f1e36c54b722d22c45 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 15:50:46 -0400 Subject: [PATCH 191/242] RequirementMachine: Don't assert on abstract conformance in desugarConformanceRequirement() --- lib/AST/RequirementMachine/RequirementLowering.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 417c1751a7f7b..297a7af55caf8 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -168,13 +168,13 @@ static void desugarConformanceRequirement(Type subjectType, Type constraintType, errors.push_back(RequirementError::forRedundantRequirement( {RequirementKind::Conformance, subjectType, constraintType}, loc)); - assert(conformance.isConcrete()); - auto *concrete = conformance.getConcrete(); - - // Introduce conditional requirements if the subject type is concrete. - for (auto req : concrete->getConditionalRequirements()) { - desugarRequirement(req, result, errors); + if (conformance.isConcrete()) { + // Introduce conditional requirements if the conformance is concrete. + for (auto req : conformance.getConcrete()->getConditionalRequirements()) { + desugarRequirement(req, result, errors); + } } + return; } From 4d531aea9c1bd0ceea07de97f7e2442b16eafa16 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 14:11:52 -0400 Subject: [PATCH 192/242] RequirementMachine: Remove PropertyMap::ConcreteConformances --- lib/AST/RequirementMachine/ConcreteTypeWitness.cpp | 11 +---------- lib/AST/RequirementMachine/PropertyMap.h | 6 ------ 2 files changed, 1 insertion(+), 16 deletions(-) diff --git a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp index 9b43018374a7e..c515074adcdd2 100644 --- a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp +++ b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp @@ -132,9 +132,7 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent( // // This occurs when a pair of rules are inherited from the property map // entry for this key's suffix. - auto pair = std::make_pair(concreteRuleID, conformanceRuleID); - auto found = ConcreteConformances.find(pair); - if (found != ConcreteConformances.end()) + if (!checkRulePairOnce(concreteRuleID, conformanceRuleID)) continue; // FIXME: Either remove the ModuleDecl entirely from conformance lookup, @@ -168,13 +166,6 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent( // opaque result type? assert(!conformance.isAbstract()); - // Save this conformance for later. - auto *concrete = conformance.getConcrete(); - auto inserted = ConcreteConformances.insert( - std::make_pair(pair, concrete)); - assert(inserted.second); - (void) inserted; - auto concreteConformanceSymbol = Symbol::forConcreteConformance( concreteType, substitutions, proto, Context); diff --git a/lib/AST/RequirementMachine/PropertyMap.h b/lib/AST/RequirementMachine/PropertyMap.h index 050699f3e440e..8758748513d42 100644 --- a/lib/AST/RequirementMachine/PropertyMap.h +++ b/lib/AST/RequirementMachine/PropertyMap.h @@ -174,12 +174,6 @@ class PropertyMap { // To avoid wasted work from re-introducing the same induced rules, // we track the rules we've seen already on previous builds. - /// Maps a pair of rules where the first is a conformance rule and the - /// second is a superclass or concrete type rule, to a concrete - /// conformance. - llvm::DenseMap, ProtocolConformance *> - ConcreteConformances; - /// Superclass requirements always imply a layout requirement, and /// concrete type requirements where the type is a class imply a /// superclass requirement. From 2f727d6b472d136191fafe40b27e3dcfd62d4be6 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Wed, 16 Mar 2022 13:31:08 -0400 Subject: [PATCH 193/242] RequirementMachine: Opaque archetype support (sort of) Complete support is behind a flag, because it can result in a non-convergent rewrite system if the opaque result type has a recursive conformance of its own (eg, `some View` for SwiftUI's View protocol). Without the flag, it's good enough for simple examples; you just can't have a requirement that mentions a nested type of a type parameter equated to the concrete type. Fixes rdar://problem/88135291, https://bugs.swift.org/browse/SR-15983. --- include/swift/Basic/LangOptions.h | 5 ++ include/swift/Option/FrontendOptions.td | 3 + .../ConcreteTypeWitness.cpp | 61 ++++++++++---- lib/AST/RequirementMachine/PropertyMap.h | 2 +- lib/Frontend/CompilerInvocation.cpp | 3 + ...paque_archetype_concrete_requirement.swift | 81 +++++++++++++++++++ ...etype_concrete_requirement_recursive.swift | 36 +++++++++ ...pported_recursive_opaque_conformance.swift | 4 +- 8 files changed, 175 insertions(+), 20 deletions(-) create mode 100644 test/Generics/opaque_archetype_concrete_requirement.swift create mode 100644 test/Generics/opaque_archetype_concrete_requirement_recursive.swift rename validation-test/{compiler_crashers_2 => compiler_crashers_2_fixed}/unsupported_recursive_opaque_conformance.swift (70%) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index c73be27773a30..339b5188720e8 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -546,6 +546,11 @@ namespace swift { /// if you have a testcase which requires this, please submit a bug report. bool EnableRequirementMachineLoopNormalization = false; + /// Enable experimental, more correct support for opaque result types as + /// concrete types. This will sometimes fail to produce a convergent + /// rewrite system. + bool EnableRequirementMachineOpaqueArchetypes = false; + /// Enables dumping type witness systems from associated type inference. bool DumpTypeWitnessSystems = false; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 778f562cff7c8..82fe523892904 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -362,6 +362,9 @@ def disable_requirement_machine_concrete_contraction : Flag<["-"], "disable-requ def enable_requirement_machine_loop_normalization : Flag<["-"], "enable-requirement-machine-loop-normalization">, HelpText<"Enable stronger minimization algorithm, for debugging only">; +def enable_requirement_machine_opaque_archetypes : Flag<["-"], "enable-requirement-machine-opaque-archetypes">, + HelpText<"Enable more correct opaque archetype support, which is off by default because it might fail to produce a convergent rewrite system">; + def dump_type_witness_systems : Flag<["-"], "dump-type-witness-systems">, HelpText<"Enables dumping type witness systems from associated type inference">; diff --git a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp index c515074adcdd2..c2578ff7e9c17 100644 --- a/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp +++ b/lib/AST/RequirementMachine/ConcreteTypeWitness.cpp @@ -162,33 +162,44 @@ void PropertyMap::concretizeNestedTypesFromConcreteParent( continue; } - // FIXME: Maybe this can happen if the concrete type is an - // opaque result type? - assert(!conformance.isAbstract()); - auto concreteConformanceSymbol = Symbol::forConcreteConformance( concreteType, substitutions, proto, Context); recordConcreteConformanceRule(concreteRuleID, conformanceRuleID, requirementKind, concreteConformanceSymbol); + // This is disabled by default because we fail to produce a convergent + // rewrite system if the opaque archetype has infinitely-recursive + // nested types. Fixing this requires a better representation for + // concrete conformances in the rewrite system. + if (conformance.isAbstract() && + !Context.getASTContext().LangOpts.EnableRequirementMachineOpaqueArchetypes) { + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "^^ " << "Skipping abstract conformance of " + << concreteType << " to " << proto->getName() << "\n"; + } + + continue; + } + for (auto *assocType : proto->getAssociatedTypeMembers()) { concretizeTypeWitnessInConformance(key, requirementKind, concreteConformanceSymbol, - concrete, assocType); + conformance, assocType); } // We only infer conditional requirements in top-level generic signatures, // not in protocol requirement signatures. - if (key.getRootProtocol() == nullptr) - inferConditionalRequirements(concrete, substitutions); + if (conformance.isConcrete() && + key.getRootProtocol() == nullptr) + inferConditionalRequirements(conformance.getConcrete(), substitutions); } } void PropertyMap::concretizeTypeWitnessInConformance( Term key, RequirementKind requirementKind, Symbol concreteConformanceSymbol, - ProtocolConformance *concrete, + ProtocolConformanceRef conformance, AssociatedTypeDecl *assocType) const { auto concreteType = concreteConformanceSymbol.getConcreteType(); auto substitutions = concreteConformanceSymbol.getSubstitutions(); @@ -202,17 +213,35 @@ void PropertyMap::concretizeTypeWitnessInConformance( << " on " << concreteType << "\n"; } - auto t = concrete->getTypeWitness(assocType); - if (!t) { - if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { - llvm::dbgs() << "^^ " << "Type witness for " << assocType->getName() - << " of " << concreteType << " could not be inferred\n"; + CanType typeWitness; + if (conformance.isConcrete()) { + auto t = conformance.getConcrete()->getTypeWitness(assocType); + + if (!t) { + if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { + llvm::dbgs() << "^^ " << "Type witness for " << assocType->getName() + << " of " << concreteType << " could not be inferred\n"; + } + + t = ErrorType::get(concreteType); } - t = ErrorType::get(concreteType); - } + typeWitness = t->getCanonicalType(); + } else if (conformance.isAbstract()) { + auto archetype = concreteType->getAs(); + if (archetype == nullptr) { + llvm::errs() << "Should only have an abstract conformance with an " + << "opaque archetype type\n"; + llvm::errs() << "Symbol: " << concreteConformanceSymbol << "\n"; + llvm::errs() << "Term: " << key << "\n"; + dump(llvm::errs()); + abort(); + } - auto typeWitness = t->getCanonicalType(); + typeWitness = archetype->getNestedType(assocType)->getCanonicalType(); + } else if (conformance.isInvalid()) { + typeWitness = CanType(ErrorType::get(Context.getASTContext())); + } if (Debug.contains(DebugFlags::ConcretizeNestedTypes)) { llvm::dbgs() << "^^ " << "Type witness for " << assocType->getName() diff --git a/lib/AST/RequirementMachine/PropertyMap.h b/lib/AST/RequirementMachine/PropertyMap.h index 8758748513d42..3f2c134b89aeb 100644 --- a/lib/AST/RequirementMachine/PropertyMap.h +++ b/lib/AST/RequirementMachine/PropertyMap.h @@ -285,7 +285,7 @@ class PropertyMap { void concretizeTypeWitnessInConformance( Term key, RequirementKind requirementKind, Symbol concreteConformanceSymbol, - ProtocolConformance *concrete, + ProtocolConformanceRef conformance, AssociatedTypeDecl *assocType) const; void inferConditionalRequirements( diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 9e9cddc9f2ae7..cfa19243647f2 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1004,6 +1004,9 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, if (Args.hasArg(OPT_enable_requirement_machine_loop_normalization)) Opts.EnableRequirementMachineLoopNormalization = true; + if (Args.hasArg(OPT_enable_requirement_machine_opaque_archetypes)) + Opts.EnableRequirementMachineOpaqueArchetypes = true; + Opts.DumpTypeWitnessSystems = Args.hasArg(OPT_dump_type_witness_systems); return HadError || UnsupportedOS || UnsupportedArch; diff --git a/test/Generics/opaque_archetype_concrete_requirement.swift b/test/Generics/opaque_archetype_concrete_requirement.swift new file mode 100644 index 0000000000000..db28a679a52e3 --- /dev/null +++ b/test/Generics/opaque_archetype_concrete_requirement.swift @@ -0,0 +1,81 @@ +// RUN: %target-swift-frontend -typecheck -verify %s -disable-availability-checking -debug-generic-signatures -requirement-machine-inferred-signatures=on -enable-requirement-machine-opaque-archetypes 2>&1 | %FileCheck %s + +protocol P1 { + associatedtype T : P2 + associatedtype U +} + +struct S_P1 : P1 { + typealias T = S_P2 + typealias U = Int +} + +protocol P2 {} + +struct S_P2 : P2 {} + +protocol P { + associatedtype T + + var t: T { get } +} + +struct DefinesOpaqueP1 : P { + var t: some P1 { + return S_P1() + } +} + +struct ConcreteHasP {} + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=ConcreteHasP +// CHECK-NEXT: Generic signature: +extension ConcreteHasP where T == DefinesOpaqueP1.T, TT == T.T, TU == T.U { + func checkSameType1(_ t: TT) -> DefinesOpaqueP1.T.T { return t } + func checkSameType2(_ u: TU) -> DefinesOpaqueP1.T.U { return u } + + func checkSameType3(_ t: T.T) -> DefinesOpaqueP1.T.T { return t } + func checkSameType4(_ u: T.U) -> DefinesOpaqueP1.T.U { return u } +} + +struct G {} + +protocol HasP { + associatedtype T : P1 + associatedtype U +} + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=HasP +// CHECK-NEXT: Generic signature: > +extension HasP where T == DefinesOpaqueP1.T, U == G { + func checkSameType1(_ t: T.T) -> DefinesOpaqueP1.T.T { return t } + func checkSameType2(_ u: T.U) -> DefinesOpaqueP1.T.U { return u } +} + +// FIXME: This does not work with -enable-requirement-machine-opaque-archetypes. +// See opaque_archetype_concrete_requirement_recursive.swift for a demonstration +// that it works without the flag (but more involved examples like the above +// won't work). + +protocol RecursiveP { + associatedtype T : RecursiveP +} + +struct S_RecursiveP : RecursiveP { + typealias T = S_RecursiveP +} + +struct DefinesRecursiveP : P { + var t: some RecursiveP { + return S_RecursiveP() + } +} + +protocol HasRecursiveP { + associatedtype T : RecursiveP +} + +extension HasRecursiveP where T == DefinesRecursiveP.T {} +// expected-error@-1 {{cannot build rewrite system for generic signature; rule length limit exceeded}} +// expected-note@-2 {{failed rewrite rule is τ_0_0.[HasRecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[concrete: ((((((((((some RecursiveP).T).T).T).T).T).T).T).T).T).T] => τ_0_0.[HasRecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T].[RecursiveP:T]}} + diff --git a/test/Generics/opaque_archetype_concrete_requirement_recursive.swift b/test/Generics/opaque_archetype_concrete_requirement_recursive.swift new file mode 100644 index 0000000000000..d6db0d55a03a8 --- /dev/null +++ b/test/Generics/opaque_archetype_concrete_requirement_recursive.swift @@ -0,0 +1,36 @@ +// RUN: %target-swift-frontend -typecheck -verify %s -disable-availability-checking -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s + +protocol P { + associatedtype T + + var t: T { get } +} + +// FIXME: This does not work with -enable-requirement-machine-opaque-archetypes. +// See opaque_archetype_concrete_requirement.swift for a demonstration that it +// fails with the flag. + +protocol RecursiveP { + associatedtype T : RecursiveP +} + +struct S_RecursiveP : RecursiveP { + typealias T = S_RecursiveP +} + +struct DefinesRecursiveP : P { + var t: some RecursiveP { + return S_RecursiveP() + } +} + +protocol HasRecursiveP { + associatedtype T : RecursiveP +} + +// CHECK-LABEL: ExtensionDecl line={{.*}} base=HasRecursiveP +// CHECK-NEXT: Generic signature: +extension HasRecursiveP where T == DefinesRecursiveP.T { + func checkSameType1(_ t: T) -> DefinesRecursiveP.T { return t } + func checkSameType2(_ t: T.T) -> DefinesRecursiveP.T.T { return t } +} diff --git a/validation-test/compiler_crashers_2/unsupported_recursive_opaque_conformance.swift b/validation-test/compiler_crashers_2_fixed/unsupported_recursive_opaque_conformance.swift similarity index 70% rename from validation-test/compiler_crashers_2/unsupported_recursive_opaque_conformance.swift rename to validation-test/compiler_crashers_2_fixed/unsupported_recursive_opaque_conformance.swift index 58f10d25472ca..dfcac93afbb10 100644 --- a/validation-test/compiler_crashers_2/unsupported_recursive_opaque_conformance.swift +++ b/validation-test/compiler_crashers_2_fixed/unsupported_recursive_opaque_conformance.swift @@ -1,6 +1,4 @@ -// RUN: not --crash %target-swift-frontend -disable-availability-checking -emit-ir -enable-parameterized-protocol-types %s - -// REQUIRES: asserts +// RUN: %target-swift-frontend -disable-availability-checking -emit-ir -enable-parameterized-protocol-types %s protocol P { var x: X { get } From 2b6379de24f6b508abe08af8b0c14be5f865e6ad Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Mar 2022 15:02:14 -0700 Subject: [PATCH 194/242] [AST] GenericSignature: Add a way to drop marker protocol requirements --- include/swift/AST/GenericSignature.h | 4 ++++ lib/AST/GenericSignature.cpp | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/include/swift/AST/GenericSignature.h b/include/swift/AST/GenericSignature.h index 9380e9f9296b9..2a578351e97d7 100644 --- a/include/swift/AST/GenericSignature.h +++ b/include/swift/AST/GenericSignature.h @@ -122,6 +122,10 @@ class GenericSignature { ArrayRef requirements, bool isKnownCanonical = false); + /// Produce a new generic signature which drops all of the marker + /// protocol conformance requirements associated with this one. + GenericSignature withoutMarkerProtocols() const; + public: static ASTContext &getASTContext(TypeArrayView params, ArrayRef requirements); diff --git a/lib/AST/GenericSignature.cpp b/lib/AST/GenericSignature.cpp index 07b3dc946cd50..ec4befcca0b23 100644 --- a/lib/AST/GenericSignature.cpp +++ b/lib/AST/GenericSignature.cpp @@ -1163,3 +1163,24 @@ swift::buildGenericSignature(ASTContext &ctx, addedRequirements}, GenericSignatureWithError()).getPointer(); } + +GenericSignature GenericSignature::withoutMarkerProtocols() const { + auto requirements = getRequirements(); + SmallVector reducedRequirements; + + // Drop all conformance requirements to marker protocols (if any). + llvm::copy_if(requirements, std::back_inserter(reducedRequirements), + [](const Requirement &requirement) { + if (requirement.getKind() == RequirementKind::Conformance) { + auto *protocol = requirement.getProtocolDecl(); + return !protocol->isMarkerProtocol(); + } + return true; + }); + + // If nothing changed, let's return this signature back. + if (requirements.size() == reducedRequirements.size()) + return *this; + + return GenericSignature::get(getGenericParams(), reducedRequirements); +} From 1330e345b74646f913206b82d72169b22c021f31 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Mar 2022 15:03:15 -0700 Subject: [PATCH 195/242] [Distributed] IRGen: Drop marker protocol requirements from accessible function generic environments Since marker protocol do not exist at runtime, it's harmful to keep them in a generic environment because they cannot be checked. Resolves: rdar://90293494 --- lib/IRGen/GenDecl.cpp | 5 +++- ...moteCallTarget_demanglingTargetNames.swift | 26 ++++++++++++++----- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index aaa6938c1ade9..09db8318c6827 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -4172,7 +4172,10 @@ void IRGenModule::emitAccessibleFunctions() { GenericSignature signature; if (auto *env = func->getGenericEnvironment()) { - signature = env->getGenericSignature(); + // Drop all of the marker protocols because they are effect-less + // at runtime. + signature = env->getGenericSignature().withoutMarkerProtocols(); + genericEnvironment = getAddrOfGenericEnvironment(signature.getCanonicalSignature()); } diff --git a/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift b/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift index 63185ffa7d762..d31bdb668a909 100644 --- a/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift +++ b/test/Distributed/Runtime/distributed_actor_remoteCallTarget_demanglingTargetNames.swift @@ -19,6 +19,9 @@ import FakeDistributedActorSystems typealias DefaultDistributedActorSystem = FakeRoundtripActorSystem +protocol SomeProtocol {} +extension String: SomeProtocol {} + distributed actor Greeter { distributed func noParams() {} distributed func noParamsThrows() throws {} @@ -28,9 +31,9 @@ distributed actor Greeter { distributed func oneLabel(value: String, _ value2: String, _ value3: String) {} distributed func parameterSingle(first: String) {} distributed func parameterPair(first: String, second: Int) {} - // FIXME(distributed): rdar://90293494 fails to get -// distributed func generic(first: A) {} -// distributed func genericNoLabel(_ first: A) {} + distributed func generic(first: A) {} + distributed func genericThree(first: A) {} + distributed func genericThreeTwo(first: A, second: B) {} } extension Greeter { distributed func parameterTriple(first: String, second: Int, third: Double) {} @@ -65,9 +68,14 @@ func test() async throws { _ = try await greeter.parameterTriple(first: "X", second: 2, third: 3.0) // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.parameterTriple(first:second:third:) - // FIXME: rdar://90293494 seems to fail getting the substitutions? -// _ = try await greeter.generic(first: "X") -// // TODO: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.parameterTriple(first:second:third:) + _ = try await greeter.generic(first: "X") + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.generic(first:) + + _ = try await greeter.genericThree(first: "X") + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.genericThree(first:) + + _ = try await greeter.genericThreeTwo(first: "X", second: "SecondValue") + // CHECK: >> remoteCallVoid: on:main.Greeter, target:main.Greeter.genericThreeTwo(first:second:) print("done") // CHECK: done @@ -75,6 +83,10 @@ func test() async throws { @main struct Main { static func main() async { - try! await test() + do { + try await test() + } catch { + print("ERROR: \(error)") + } } } From ab030050b0a15242a304407c9d27ab2bd768a268 Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Thu, 17 Mar 2022 15:38:25 -0700 Subject: [PATCH 196/242] [Distributed] NFC: Add accessor section test-case to make sure that marker protocols are not mangled --- .../distributed_actor_accessor_section_coff.swift | 10 ++++++++++ .../distributed_actor_accessor_section_elf.swift | 10 ++++++++++ .../distributed_actor_accessor_section_macho.swift | 10 ++++++++++ 3 files changed, 30 insertions(+) diff --git a/test/Distributed/distributed_actor_accessor_section_coff.swift b/test/Distributed/distributed_actor_accessor_section_coff.swift index bd2a5d032ce7d..c0b99f6451353 100644 --- a/test/Distributed/distributed_actor_accessor_section_coff.swift +++ b/test/Distributed/distributed_actor_accessor_section_coff.swift @@ -76,6 +76,10 @@ public distributed actor MyActor { distributed func complex(_: [Int], _: Obj, _: String?, _: LargeStruct) -> LargeStruct { fatalError() } + + // Make sure that Sendable doesn't show up in the mangled name + distributed func generic(_: T) { + } } @available(SwiftStdlib 5.7, *) @@ -123,6 +127,12 @@ public distributed actor MyOtherActor { // CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtYaKFTETFTu" to i{{32|64}}) // CHECK-SAME: , section ".sw5acfn$B", {{.*}} +/// -> `MyActor.generic` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7genericyyxYaKSeRzSERzlFTEHF" = private constant +// CHECK-SAME: @"symbolic x___________pSeRzSERzlIetMHngzo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7genericyyxYaKSeRzSERzlFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section ".sw5acfn$B", {{.*}} + /// -> `MyOtherActor.empty` // CHECK: @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyYaKFTEHF" = private constant // CHECK-SAME: @"symbolic ___________pIetMHgzo_ 27distributed_actor_accessors12MyOtherActorC s5ErrorP" diff --git a/test/Distributed/distributed_actor_accessor_section_elf.swift b/test/Distributed/distributed_actor_accessor_section_elf.swift index 9ccf828abdaae..f968852aa4b8c 100644 --- a/test/Distributed/distributed_actor_accessor_section_elf.swift +++ b/test/Distributed/distributed_actor_accessor_section_elf.swift @@ -74,6 +74,10 @@ public distributed actor MyActor { distributed func complex(_: [Int], _: Obj, _: String?, _: LargeStruct) -> LargeStruct { fatalError() } + + // Make sure that Sendable doesn't show up in the mangled name + distributed func generic(_: T) { + } } @available(SwiftStdlib 5.7, *) @@ -121,6 +125,12 @@ public distributed actor MyOtherActor { // CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtYaKFTETFTu" to i{{32|64}}) // CHECK-SAME: , section "swift5_accessible_functions", {{.*}} +/// -> `MyActor.generic` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7genericyyxYaKSeRzSERzlFTEHF" = private constant +// CHECK-SAME: @"symbolic x___________pSeRzSERzlIetMHngzo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7genericyyxYaKSeRzSERzlFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section "swift5_accessible_functions", {{.*}} + /// -> `MyOtherActor.empty` // CHECK: @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyYaKFTEHF" = private constant // CHECK-SAME: @"symbolic ___________pIetMHgzo_ 27distributed_actor_accessors12MyOtherActorC s5ErrorP" diff --git a/test/Distributed/distributed_actor_accessor_section_macho.swift b/test/Distributed/distributed_actor_accessor_section_macho.swift index 3974f811ec16f..5dffb0a56ebad 100644 --- a/test/Distributed/distributed_actor_accessor_section_macho.swift +++ b/test/Distributed/distributed_actor_accessor_section_macho.swift @@ -74,6 +74,10 @@ public distributed actor MyActor { distributed func complex(_: [Int], _: Obj, _: String?, _: LargeStruct) -> LargeStruct { fatalError() } + + // Make sure that Sendable doesn't show up in the mangled name + distributed func generic(_: T) { + } } @available(SwiftStdlib 5.7, *) @@ -121,6 +125,12 @@ public distributed actor MyOtherActor { // CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7complexyAA11LargeStructVSaySiG_AA3ObjCSSSgAFtYaKFTETFTu" to i{{32|64}}) // CHECK-SAME: , section {{"swift5_accessible_functions"|".sw5acfn$B"|"__TEXT, __swift5_acfuncs, regular"}} +/// -> `MyActor.generic` +// CHECK: @"$s27distributed_actor_accessors7MyActorC7genericyyxYaKSeRzSERzlFTEHF" = private constant +// CHECK-SAME: @"symbolic x___________pSeRzSERzlIetMHngzo_ 27distributed_actor_accessors7MyActorC s5ErrorP" +// CHECK-SAME: (%swift.async_func_pointer* @"$s27distributed_actor_accessors7MyActorC7genericyyxYaKSeRzSERzlFTETFTu" to i{{32|64}}) +// CHECK-SAME: , section {{"swift5_accessible_functions"|".sw5acfn$B"|"__TEXT, __swift5_acfuncs, regular"}} + /// -> `MyOtherActor.empty` // CHECK: @"$s27distributed_actor_accessors12MyOtherActorC5emptyyyYaKFTEHF" = private constant // CHECK-SAME: @"symbolic ___________pIetMHgzo_ 27distributed_actor_accessors12MyOtherActorC s5ErrorP" From 0e4e491f223803a2db43c613234d0d0b20739866 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Wed, 16 Mar 2022 16:23:18 -0700 Subject: [PATCH 197/242] Add SIL Test for Parameterized Existential Conversions --- test/SILGen/existential_parameters.swift | 117 +++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 test/SILGen/existential_parameters.swift diff --git a/test/SILGen/existential_parameters.swift b/test/SILGen/existential_parameters.swift new file mode 100644 index 0000000000000..849d7eb76eddb --- /dev/null +++ b/test/SILGen/existential_parameters.swift @@ -0,0 +1,117 @@ +// RUN: %target-swift-emit-silgen -module-name parameterized -enable-parameterized-protocol-types %s | %FileCheck %s + +protocol P {} + +protocol Q : P {} + +struct S: Q { + typealias T = Int + typealias U = String + typealias V = Float + + typealias X = Int + typealias Y = String + typealias Z = Float +} + +// CHECK-LABEL: sil hidden [ossa] @$s13parameterized6upcastyAA1P_pAA1SVF : $@convention(thin) (S) -> @out P { +func upcast(_ x: S) -> any P { + // CHECK: bb0([[RESULT_PARAM:%.*]] : $*P, [[CONCRETE_VAL:%.*]] : $S): + // CHECK: [[Q_INT_STRING_FLOAT:%.*]] = alloc_stack $Q + // CHECK: [[INIT_Q_INT_STRING_FLOAT:%.*]] = init_existential_addr [[Q_INT_STRING_FLOAT]] : $*Q, $S + // CHECK: store [[CONCRETE_VAL]] to [trivial] [[INIT_Q_INT_STRING_FLOAT]] : $*S + // CHECK: [[OPEN_Q_INT_STRING_FLOAT:%.*]] = open_existential_addr immutable_access [[Q_INT_STRING_FLOAT]] : $*Q to $*[[OPENED_Q_INT_STRING_FLOAT:@opened(.*) Q]] + // CHECK: [[RESULT_INIT:%.*]] = init_existential_addr [[RESULT_PARAM]] : $*P, $[[OPENED_Q_INT_STRING_FLOAT]] + // CHECK: copy_addr [[OPEN_Q_INT_STRING_FLOAT]] to [initialization] [[RESULT_INIT]] : $*[[OPENED_Q_INT_STRING_FLOAT]] + + return x as any Q as any P +} + +// CHECK-LABEL: sil hidden [ossa] @$s13parameterized12upupupupcastyAA1P_pAA1SVF : $@convention(thin) (S) -> @out P { +func upupupupcast(_ x: S) -> any P { + // CHECK: bb0([[RESULT_PARAM:%.*]] : $*P, [[CONCRETE_VAL:%.*]] : $S): + + // CHECK: [[P_INT_STRING_FLOAT:%.*]] = alloc_stack $P + // CHECK: [[INIT_INT_STRING_FLOAT:%.*]] = init_existential_addr [[P_INT_STRING_FLOAT]] : $*P, $S + // CHECK: store [[CONCRETE_VAL]] to [trivial] [[INIT_INT_STRING_FLOAT]] : $*S + // CHECK: [[OPEN_INT_STRING_FLOAT:%.*]] = open_existential_addr immutable_access %3 : $*P to $*[[OPENED_P_INT_STRING_FLOAT:@opened(.*) P]] + + // CHECK: [[P_INT_STRING:%.*]] = alloc_stack $P + // CHECK: [[INIT_INT_STRING:%.*]] = init_existential_addr [[P_INT_STRING]] : $*P, $[[OPENED_P_INT_STRING_FLOAT]] + // CHECK: copy_addr [[OPEN_INT_STRING_FLOAT]] to [initialization] [[INIT_INT_STRING]] : $*[[OPENED_P_INT_STRING_FLOAT]] + // CHECK: [[OPEN_INT_STRING:%.*]] = open_existential_addr immutable_access [[P_INT_STRING]] : $*P to $*[[OPENED_P_INT_STRING:@opened(.*) P]] + + // CHECK: [[P_INT:%.*]] = alloc_stack $P + // CHECK: [[INIT_INT:%.*]] = init_existential_addr [[P_INT]] : $*P, $[[OPENED_P_INT_STRING]] + // CHECK: copy_addr [[OPEN_INT_STRING]] to [initialization] [[INIT_INT]] : $*[[OPENED_P_INT_STRING]] + // CHECK: [[OPEN_INT:%.*]] = open_existential_addr immutable_access [[P_INT]] : $*P to $*[[OPENED_P_INT:@opened(.*) P]] + + // CHECK: [[RESULT_INIT:%.*]] = init_existential_addr [[RESULT_PARAM]] : $*P, $[[OPENED_P_INT]] + // CHECK: copy_addr [[OPEN_INT]] to [initialization] [[RESULT_INIT]] : $*[[OPENED_P_INT]] + return x as any P as any P as any P as any P +} + +func use(_ k: (S) -> Void) {} + +// CHECK-LABEL: sil hidden [ossa] @$s13parameterized11upcastInputyyF : $@convention(thin) () -> () { +func upcastInput() { + // CHECK: [[INT_STRING_FN:%.*]] = function_ref @$s13parameterized11upcastInputyyFyAA1P_pySiSSXPXEfU_ : $@convention(thin) (@in_guaranteed P) -> () + // CHECK: [[NOESCAPE_INT_STRING_FN:%.*]] = convert_function [[INT_STRING_FN]] : $@convention(thin) (@in_guaranteed P) -> () to $@convention(thin) @noescape (@in_guaranteed P) -> () + // CHECK: [[THICK_INT_STRING_FN:%.*]] = thin_to_thick_function [[NOESCAPE_INT_STRING_FN]] : $@convention(thin) @noescape (@in_guaranteed P) -> () to $@noescape @callee_guaranteed (@in_guaranteed P) -> () + // CHECK: [[S_TO_INT_STRING_THUNK_FN:%.*]] = function_ref @$s13parameterized1P_pySiSSXPIgn_AA1SVIegy_TR : $@convention(thin) (S, @noescape @callee_guaranteed (@in_guaranteed P) -> ()) -> () + // CHECK: [[PARTIAL_INT_STRING_THUNK_FN:%.*]] = partial_apply [callee_guaranteed] [[S_TO_INT_STRING_THUNK_FN]]([[THICK_INT_STRING_FN]]) : $@convention(thin) (S, @noescape @callee_guaranteed (@in_guaranteed P) -> ()) -> () + // CHECK: [[NOESCAPE_INT_STRING_THUNK_FN:%.*]] = convert_escape_to_noescape [not_guaranteed] [[PARTIAL_INT_STRING_THUNK_FN]] : $@callee_guaranteed (S) -> () to $@noescape @callee_guaranteed (S) -> () + // CHECK: [[USE_FN:%.*]] = function_ref @$s13parameterized3useyyyAA1SVXEF : $@convention(thin) (@noescape @callee_guaranteed (S) -> ()) -> () + // CHECK: {{%.*}} = apply [[USE_FN]]([[NOESCAPE_INT_STRING_THUNK_FN]]) : $@convention(thin) (@noescape @callee_guaranteed (S) -> ()) -> () + + use({ (p: any P) -> Void in }) + + // CHECK: [[INT_FN:%.*]] = function_ref @$s13parameterized11upcastInputyyFyAA1P_pySiXPXEfU0_ : $@convention(thin) (@in_guaranteed P) -> () + // CHECK: [[NOESCAPE_INT_FN:%.*]] = convert_function [[INT_FN]] : $@convention(thin) (@in_guaranteed P) -> () to $@convention(thin) @noescape (@in_guaranteed P) -> () + // CHECK: [[THICK_INT_FN:%.*]] = thin_to_thick_function [[NOESCAPE_INT_FN]] : $@convention(thin) @noescape (@in_guaranteed P) -> () to $@noescape @callee_guaranteed (@in_guaranteed P) -> () + // CHECK: [[S_TO_INT_THUNK_FN:%.*]] = function_ref @$s13parameterized1P_pySiXPIgn_AA1SVIegy_TR : $@convention(thin) (S, @noescape @callee_guaranteed (@in_guaranteed P) -> ()) -> () + // CHECK: [[PARTIAL_INT_THUNK_FN:%.*]] = partial_apply [callee_guaranteed] [[S_TO_INT_THUNK_FN]]([[THICK_INT_FN]]) : $@convention(thin) (S, @noescape @callee_guaranteed (@in_guaranteed P) -> ()) -> () + // CHECK: [[NOESCAPE_INT_THUNK_FN:%.*]] = convert_escape_to_noescape [not_guaranteed] [[PARTIAL_INT_THUNK_FN]] : $@callee_guaranteed (S) -> () to $@noescape @callee_guaranteed (S) -> () + // CHECK: [[USE_FN:%.*]] = function_ref @$s13parameterized3useyyyAA1SVXEF : $@convention(thin) (@noescape @callee_guaranteed (S) -> ()) -> () + // CHECK: {{%.*}} = apply [[USE_FN]]([[NOESCAPE_INT_THUNK_FN]]) : $@convention(thin) (@noescape @callee_guaranteed (S) -> ()) -> () + + use({ (p: any P) -> Void in }) + + // CHECK: [[P_FN:%.*]] = function_ref @$s13parameterized11upcastInputyyFyAA1P_pXEfU1_ : $@convention(thin) (@in_guaranteed P) -> () + // CHECK: [[NOESCAPE_P_FN:%.*]] = convert_function [[P_FN]] : $@convention(thin) (@in_guaranteed P) -> () to $@convention(thin) @noescape (@in_guaranteed P) -> () + // CHECK: [[THICK_P_FN:%.*]] = thin_to_thick_function [[NOESCAPE_P_FN]] : $@convention(thin) @noescape (@in_guaranteed P) -> () to $@noescape @callee_guaranteed (@in_guaranteed P) -> () + // CHECK: [[S_TO_P_THUNK_FN:%.*]] = function_ref @$s13parameterized1P_pIgn_AA1SVIegy_TR : $@convention(thin) (S, @noescape @callee_guaranteed (@in_guaranteed P) -> ()) -> () + // CHECK: [[PARTIAL_INT_THUNK_FN:%.*]] = partial_apply [callee_guaranteed] [[S_TO_P_THUNK_FN]]([[THICK_P_FN]]) : $@convention(thin) (S, @noescape @callee_guaranteed (@in_guaranteed P) -> ()) -> () + // CHECK: [[NOESCAPE_P_THUNK_FN:%.*]] = convert_escape_to_noescape [not_guaranteed] [[PARTIAL_INT_THUNK_FN]] : $@callee_guaranteed (S) -> () to $@noescape @callee_guaranteed (S) -> () + // CHECK: [[USE_FN:%.*]] = function_ref @$s13parameterized3useyyyAA1SVXEF : $@convention(thin) (@noescape @callee_guaranteed (S) -> ()) -> () + // CHECK: {{%.*}} = apply [[USE_FN]]([[NOESCAPE_P_THUNK_FN]]) : $@convention(thin) (@noescape @callee_guaranteed (S) -> ()) -> () + + use({ (p: any P) -> Void in }) +} + +func reuse(_ k: () -> any P) {} + +// CHECK-LABEL: sil hidden [ossa] @$s13parameterized12upcastResultyyF : $@convention(thin) () -> () { +func upcastResult() { + // CHECK: [[RES_FN:%.*]] = function_ref @$s13parameterized12upcastResultyyFAA1SVyXEfU_ : $@convention(thin) () -> S + // CHECK: [[NOESCAPE_RES_FN:%.*]] = convert_function [[RES_FN]] : $@convention(thin) () -> S to $@convention(thin) @noescape () -> S + // CHECK: [[THICK_RES_FN:%.*]] = thin_to_thick_function [[NOESCAPE_RES_FN]] : $@convention(thin) @noescape () -> S to $@noescape @callee_guaranteed () -> S + // CHECK: [[S_TO_P_RES_THUNK_FN:%.*]] = function_ref @$s13parameterized1SVIgd_AA1P_pySiSSSfXPIegr_TR : $@convention(thin) (@noescape @callee_guaranteed () -> S) -> @out P + // CHECK: [[PARTIAL_RES_THUNK_FN:%.*]] = partial_apply [callee_guaranteed] [[S_TO_P_RES_THUNK_FN]]([[THICK_RES_FN]]) : $@convention(thin) (@noescape @callee_guaranteed () -> S) -> @out P + // CHECK: [[NOESCAPE_RES_THUNK_FN:%.*]] = convert_escape_to_noescape [not_guaranteed] [[PARTIAL_RES_THUNK_FN]] : $@callee_guaranteed () -> @out P to $@noescape @callee_guaranteed () -> @out P + // CHECK: [[REUSE_FN:%.*]] = function_ref @$s13parameterized5reuseyyAA1P_pySiSSSfXPyXEF : $@convention(thin) (@noescape @callee_guaranteed () -> @out P) -> () + // CHECK: {{%.*}} = apply [[REUSE_FN]]([[NOESCAPE_RES_THUNK_FN]]) : $@convention(thin) (@noescape @callee_guaranteed () -> @out P) -> () + + reuse({ () -> S in S() }) + + // CHECK: [[RES_Q_FN:%.*]] = function_ref @$s13parameterized12upcastResultyyFAA1Q_pySiSSSfXPyXEfU0_ : $@convention(thin) () -> @out Q + // CHECK: [[NOESCAPE_RES_Q_FN:%.*]] = convert_function [[RES_Q_FN]] : $@convention(thin) () -> @out Q to $@convention(thin) @noescape () -> @out Q + // CHECK: [[THICK_NOESCAPE_RES_Q_FN:%.*]] = thin_to_thick_function [[NOESCAPE_RES_Q_FN]] : $@convention(thin) @noescape () -> @out Q to $@noescape @callee_guaranteed () -> @out Q + // CHECK: [[P_TO_Q_RES_THUNK_FN:%.*]] = function_ref @$s13parameterized1Q_pySiSSSfXPIgr_AA1P_pySiSSSfXPIegr_TR : $@convention(thin) (@noescape @callee_guaranteed () -> @out Q) -> @out P + // CHECK: [[PARTIAL_P_TO_Q_RES_THUNK_FN:%.*]] = partial_apply [callee_guaranteed] [[P_TO_Q_RES_THUNK_FN]]([[THICK_NOESCAPE_RES_Q_FN]]) : $@convention(thin) (@noescape @callee_guaranteed () -> @out Q) -> @out P + // CHECK: [[NOESCAPE_PARTIAL_P_TO_Q_RES_THUNK_FN:%.*]] = convert_escape_to_noescape [not_guaranteed] [[PARTIAL_P_TO_Q_RES_THUNK_FN]] : $@callee_guaranteed () -> @out P to $@noescape @callee_guaranteed () -> @out P + // CHECK: [[REUSE_FN:%.*]] = function_ref @$s13parameterized5reuseyyAA1P_pySiSSSfXPyXEF : $@convention(thin) (@noescape @callee_guaranteed () -> @out P) -> () + // CHECK: {{%.*}} = apply [[REUSE_FN]]([[NOESCAPE_PARTIAL_P_TO_Q_RES_THUNK_FN]]) : $@convention(thin) (@noescape @callee_guaranteed () -> @out P) -> () + + reuse({ () -> any Q in S() }) +} From 5db8647911be6d7fa591ea8386ba59e2d33cd777 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Thu, 17 Mar 2022 16:26:57 -0700 Subject: [PATCH 198/242] [cxx-interop] Make sure to use TUScope when looking up clang decls. When we look up a name directly, make sure we provide a scope, this is required when C++ interop is enabled. This issue was exposed when we look up if an NSString is hashable. There is a special case for classes that inherit from NSObject, but we didn't see that, because we couldn't find NSObject (because lookup failed). --- lib/ClangImporter/ImportType.cpp | 2 +- test/Interop/Cxx/class/Inputs/dictionary-of-nsstrings.h | 8 ++++++++ test/Interop/Cxx/class/Inputs/module.modulemap | 5 +++++ .../class/dictionary-of-nsstrings-module-interface.swift | 5 +++++ 4 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 test/Interop/Cxx/class/Inputs/dictionary-of-nsstrings.h create mode 100644 test/Interop/Cxx/class/dictionary-of-nsstrings-module-interface.swift diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 66f79cf208f27..09943645fe115 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -3071,7 +3071,7 @@ Decl *ClangImporter::Implementation::importDeclByName(StringRef name) { clang::LookupResult lookupResult(sema, clangName, clang::SourceLocation(), clang::Sema::LookupOrdinaryName); lookupResult.setAllowHidden(true); - if (!sema.LookupName(lookupResult, /*Scope=*/nullptr)) { + if (!sema.LookupName(lookupResult, /*Scope=*/sema.TUScope)) { return nullptr; } diff --git a/test/Interop/Cxx/class/Inputs/dictionary-of-nsstrings.h b/test/Interop/Cxx/class/Inputs/dictionary-of-nsstrings.h new file mode 100644 index 0000000000000..01656a753bab7 --- /dev/null +++ b/test/Interop/Cxx/class/Inputs/dictionary-of-nsstrings.h @@ -0,0 +1,8 @@ +#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_DESTRUCTORS_H +#define TEST_INTEROP_CXX_CLASS_INPUTS_DESTRUCTORS_H + +#import + +void takesDictionaryOfStrings(NSDictionary*_Nonnull a) { } + +#endif // TEST_INTEROP_CXX_CLASS_INPUTS_DESTRUCTORS_H diff --git a/test/Interop/Cxx/class/Inputs/module.modulemap b/test/Interop/Cxx/class/Inputs/module.modulemap index 60d0b459679c2..5d50a270642cd 100644 --- a/test/Interop/Cxx/class/Inputs/module.modulemap +++ b/test/Interop/Cxx/class/Inputs/module.modulemap @@ -92,3 +92,8 @@ module ClassProtocolNameClash { header "class-protocol-name-clash.h" requires cplusplus } + +module DictionaryOfNSStrings { + header "dictionary-of-nsstrings.h" + requires cplusplus +} diff --git a/test/Interop/Cxx/class/dictionary-of-nsstrings-module-interface.swift b/test/Interop/Cxx/class/dictionary-of-nsstrings-module-interface.swift new file mode 100644 index 0000000000000..ff6d18121ca9e --- /dev/null +++ b/test/Interop/Cxx/class/dictionary-of-nsstrings-module-interface.swift @@ -0,0 +1,5 @@ +// RUN: %target-swift-ide-test -print-module -module-to-print=DictionaryOfNSStrings -I %S/Inputs/ -source-filename=x -enable-cxx-interop | %FileCheck %s + +// REQUIRES: objc_interop + +// CHECK: func takesDictionaryOfStrings(_ a: [String : String]) \ No newline at end of file From 13e64f3a3f5db67b72c5b5bf36b010bc34c1ab88 Mon Sep 17 00:00:00 2001 From: zoecarver Date: Thu, 17 Mar 2022 16:54:19 -0700 Subject: [PATCH 199/242] [nfc][cxx-interop] Add some pretty stack traces for various things that crash a lot. --- include/swift/AST/PrettyStackTrace.h | 13 +++++++++++++ lib/AST/PrettyStackTrace.cpp | 13 +++++++++++++ lib/ClangImporter/ClangImporter.cpp | 3 +++ lib/ClangImporter/ImportType.cpp | 5 +++++ 4 files changed, 34 insertions(+) diff --git a/include/swift/AST/PrettyStackTrace.h b/include/swift/AST/PrettyStackTrace.h index 8b1cc80cbf431..0baaa28354cca 100644 --- a/include/swift/AST/PrettyStackTrace.h +++ b/include/swift/AST/PrettyStackTrace.h @@ -69,6 +69,19 @@ class PrettyStackTraceDecl : public llvm::PrettyStackTraceEntry { virtual void print(llvm::raw_ostream &OS) const override; }; +/// PrettyStackTraceDecl - Observe that we are processing a specific +/// declaration with a given substitution map. +class PrettyStackTraceDeclAndSubst : public llvm::PrettyStackTraceEntry { + const Decl *decl; + SubstitutionMap subst; + const char *action; +public: + PrettyStackTraceDeclAndSubst(const char *action, SubstitutionMap subst, + const Decl *decl) + : decl(decl), subst(subst), action(action) {} + virtual void print(llvm::raw_ostream &OS) const override; +}; + /// PrettyStackTraceAnyFunctionRef - Observe that we are processing a specific /// function or closure literal. class PrettyStackTraceAnyFunctionRef : public llvm::PrettyStackTraceEntry { diff --git a/lib/AST/PrettyStackTrace.cpp b/lib/AST/PrettyStackTrace.cpp index 71663b08cbc98..c4ccc40fe5692 100644 --- a/lib/AST/PrettyStackTrace.cpp +++ b/lib/AST/PrettyStackTrace.cpp @@ -42,6 +42,19 @@ void PrettyStackTraceDecl::print(llvm::raw_ostream &out) const { printDeclDescription(out, TheDecl, TheDecl->getASTContext()); } +void PrettyStackTraceDeclAndSubst::print(llvm::raw_ostream &out) const { + out << "While " << action << ' '; + if (!decl) { + out << "NULL declaration!\n"; + return; + } + printDeclDescription(out, decl, decl->getASTContext()); + + out << "with substitution map: "; + subst.dump(out); +} + + void swift::printDeclDescription(llvm::raw_ostream &out, const Decl *D, const ASTContext &Context, bool addNewline) { SourceLoc loc = D->getStartLoc(); diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 5b380b9bb8ab9..1c71dfa914304 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -29,6 +29,7 @@ #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/NameLookupRequests.h" +#include "swift/AST/PrettyStackTrace.h" #include "swift/AST/Types.h" #include "swift/Basic/Defer.h" #include "swift/Basic/Platform.h" @@ -5582,6 +5583,8 @@ static ValueDecl *generateThunkForExtraMetatypes(SubstitutionMap subst, ConcreteDeclRef ClangImporter::getCXXFunctionTemplateSpecialization(SubstitutionMap subst, ValueDecl *decl) { + PrettyStackTraceDeclAndSubst trace("specializing", subst, decl); + assert(isa(decl->getClangDecl()) && "This API should only be used with function templates."); diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 66f79cf208f27..3b095bb27f57f 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -28,6 +28,7 @@ #include "swift/AST/Module.h" #include "swift/AST/NameLookup.h" #include "swift/AST/ParameterList.h" +#include "swift/AST/PrettyStackTrace.h" #include "swift/AST/Type.h" #include "swift/AST/Types.h" #include "swift/AST/TypeVisitor.h" @@ -211,6 +212,10 @@ namespace { using TypeVisitor::Visit; ImportResult Visit(clang::QualType type) { + PrettyStackTraceClangType trace(Impl.getClangASTContext(), + "importing a clang type", + type.getTypePtr()); + auto IR = Visit(type.getTypePtr()); return IR; } From 9ad3c9a5dbf99bb3757ff1ec0f29ca4935423fc9 Mon Sep 17 00:00:00 2001 From: David Smith Date: Thu, 17 Mar 2022 14:54:05 -0700 Subject: [PATCH 200/242] Use withUnsafeTemporaryAllocation instead of a temporary Array --- stdlib/public/core/UnicodeHelpers.swift | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/stdlib/public/core/UnicodeHelpers.swift b/stdlib/public/core/UnicodeHelpers.swift index c37d9e2e312f6..13341cb542f6a 100644 --- a/stdlib/public/core/UnicodeHelpers.swift +++ b/stdlib/public/core/UnicodeHelpers.swift @@ -396,17 +396,15 @@ extension _StringGuts { ).0)) } - // TODO(String performance): Stack buffer if small enough - let cus = Array(unsafeUninitializedCapacity: count) { - buffer, initializedCapacity in + return withUnsafeTemporaryAllocation( + of: UInt16.self, capacity: count + ) { buffer in _cocoaStringCopyCharacters( from: self._object.cocoaObject, range: start.. Date: Mon, 14 Mar 2022 17:09:13 -0700 Subject: [PATCH 201/242] Add a function index This radically simplifies the representation of function sets when analyzing the entire module's call graph at once. --- include/swift/SIL/SILFunction.h | 6 ++++++ include/swift/SIL/SILModule.h | 10 ++++++++++ lib/SIL/IR/SILFunction.cpp | 5 +++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/swift/SIL/SILFunction.h b/include/swift/SIL/SILFunction.h index 1f23676b657b7..be958f54030fa 100644 --- a/include/swift/SIL/SILFunction.h +++ b/include/swift/SIL/SILFunction.h @@ -224,6 +224,10 @@ class SILFunction /// For details see BasicBlockBitfield::bitfieldID; unsigned currentBitfieldID = 1; + /// Unique identifier for vector indexing and deterministic sorting. + /// May be reused when zombie functions are recovered. + unsigned index; + /// The function's set of semantics attributes. /// /// TODO: Why is this using a std::string? Why don't we use uniqued @@ -446,6 +450,8 @@ class SILFunction return SILFunctionConventions(fnType, getModule()); } + unsigned getIndex() const { return index; } + SILProfiler *getProfiler() const { return Profiler; } SILFunction *getDynamicallyReplacedFunction() const { diff --git a/include/swift/SIL/SILModule.h b/include/swift/SIL/SILModule.h index c522cb946b86e..36fbfdcaf7ece 100644 --- a/include/swift/SIL/SILModule.h +++ b/include/swift/SIL/SILModule.h @@ -345,6 +345,10 @@ class SILModule { /// The options passed into this SILModule. const SILOptions &Options; + /// The number of functions created in this module, which will be the index of + /// the next function. + unsigned nextFunctionIndex = 0; + /// Set if the SILModule was serialized already. It is used /// to ensure that the module is serialized only once. bool serialized; @@ -449,6 +453,12 @@ class SILModule { /// Called after an instruction is moved from one function to another. void notifyMovedInstruction(SILInstruction *inst, SILFunction *fromFunction); + unsigned getNewFunctionIndex() { return nextFunctionIndex++; } + + // This may be larger that the number of live functions in the 'functions' + // linked list because it includes the indices of zombie functions. + unsigned getNumFunctionIndices() const { return nextFunctionIndex; } + /// Set a serialization action. void setSerializeSILAction(ActionCallback SerializeSILAction); ActionCallback getSerializeSILAction() const; diff --git a/lib/SIL/IR/SILFunction.cpp b/lib/SIL/IR/SILFunction.cpp index bab5b60afbaa5..cfe9aa1b4e11f 100644 --- a/lib/SIL/IR/SILFunction.cpp +++ b/lib/SIL/IR/SILFunction.cpp @@ -150,8 +150,9 @@ SILFunction::SILFunction(SILModule &Module, SILLinkage Linkage, StringRef Name, IsDynamicallyReplaceable_t isDynamic, IsExactSelfClass_t isExactSelfClass, IsDistributed_t isDistributed) - : SwiftObjectHeader(functionMetatype), - Module(Module), Availability(AvailabilityContext::alwaysAvailable()) { + : SwiftObjectHeader(functionMetatype), Module(Module), + index(Module.getNewFunctionIndex()), + Availability(AvailabilityContext::alwaysAvailable()) { init(Linkage, Name, LoweredType, genericEnv, Loc, isBareSILFunction, isTrans, isSerialized, entryCount, isThunk, classSubclassScope, inlineStrategy, E, DebugScope, isDynamic, isExactSelfClass, isDistributed); From 7d2aac6c001469a77699a52e2ca8bf57d3c59c2b Mon Sep 17 00:00:00 2001 From: zoecarver Date: Thu, 17 Mar 2022 20:47:49 -0700 Subject: [PATCH 202/242] [nfc][docs][cxx-interop] Add example in the status document. --- .../CppInteroperabilityStatus.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/docs/CppInteroperability/CppInteroperabilityStatus.md b/docs/CppInteroperability/CppInteroperabilityStatus.md index d749d5df7520e..46a7d4a89dc92 100644 --- a/docs/CppInteroperability/CppInteroperabilityStatus.md +++ b/docs/CppInteroperability/CppInteroperabilityStatus.md @@ -8,6 +8,51 @@ This document provides an overview of the status of the Swift and C++ interopera Swift has the experimental ability to import a large subset of C++. This section of the document describes which C++ language and standard library features can be imported and used from Swift in an experimental manner. +### Example +The following example demonstrates several interop features. It compiles and runs on main. + +```C++ +// cxx-types.h (mapped to CxxTypes module in module.modulemap) +#include +#include + +using V = std::vector; +``` + +```Swift +// main.swift +import CxxTypes +import std.vector +import std.algorithm + +// We can extend C++ types in Swift. +extension V : RandomAccessCollection { + public var startIndex: Int { 0 } + public var endIndex: Int { size() } +} + +// Create a vector with some data. +var numbers = V(4) +std.fill(numbers.beginMutating(), numbers.endMutating(), 41) + +// Transform it using C++. +std.transform(numbers.beginMutating(), numbers.endMutating(), + numbers.beginMutating()) { (element: Int) in + return element + 1 +} + +// Loop over it in Swift. +for (index, element) in numbers.enumerated() { + print("v[\(index)] = \(element)") +} + +// We can also use anything in RandomAccessCollection, such as map and zip. +let strings = numbers.map { "\($0)" } +for (s, n) in zip(strings, numbers) { + print("\(s) = \(n)") +} +``` + ### Importing C++ There are currently two experimental ways to import C++ into Swift: From 075e7c14253cdd2773fe250983a8e1c917b326a5 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 17 Mar 2022 20:03:02 -0400 Subject: [PATCH 203/242] RequirementMachine: Add regression test for rdar://problem/90402519 --- test/Generics/rdar90402519.swift | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test/Generics/rdar90402519.swift diff --git a/test/Generics/rdar90402519.swift b/test/Generics/rdar90402519.swift new file mode 100644 index 0000000000000..82f47adfab60d --- /dev/null +++ b/test/Generics/rdar90402519.swift @@ -0,0 +1,27 @@ +// RUN: %target-typecheck-verify-swift + +// This needs a better diagnostic. The real problem is the 'C == G' +// requirement in P2 conflicts with the one in P1. + +protocol P { + associatedtype I +} + +struct G1 : P {} +struct G2 : P { + typealias I = G +} + +struct G {} + +protocol P0 { + associatedtype C : P +} + +protocol P1 : P0 where C == G1 { + associatedtype I +} + +protocol P2 : P1 where C == G2 {} +// expected-error@-1 {{cannot build rewrite system for protocol; concrete nesting limit exceeded}} +// expected-note@-2 {{failed rewrite rule is [P2:I].[concrete: G>>>>>>>>>>>>>>>>>>>>>>>>>>>>>] => [P2:I]}} From 4e60aa150a7c06206801a4b53908a09ab2680086 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 18 Mar 2022 00:02:25 -0400 Subject: [PATCH 204/242] RequirementMachine: Pass -requirement-machine-inferred-signatures=off in a handful of tests - unify_superclass_types_[23].swift: I will fix these soon. rdar://90469643 - attr/accessibility_where_clause.swift: This one is dicey and should probably be banned. rdar://90469477 --- test/Generics/unify_superclass_types_2.swift | 48 ++++++++++-------- test/Generics/unify_superclass_types_3.swift | 52 +++++++++++--------- test/attr/accessibility.swift | 28 ----------- test/attr/accessibility_where_clause.swift | 32 ++++++++++++ 4 files changed, 88 insertions(+), 72 deletions(-) create mode 100644 test/attr/accessibility_where_clause.swift diff --git a/test/Generics/unify_superclass_types_2.swift b/test/Generics/unify_superclass_types_2.swift index 3e9e906938e5c..9e0e9b63ed378 100644 --- a/test/Generics/unify_superclass_types_2.swift +++ b/test/Generics/unify_superclass_types_2.swift @@ -1,8 +1,12 @@ -// RUN: %target-typecheck-verify-swift -dump-requirement-machine 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-inferred-signatures=off 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift -dump-requirement-machine -requirement-machine-inferred-signatures=off 2>&1 | %FileCheck %s --check-prefix=CHECK-RULE // Note: The GSB fails this test, because it doesn't implement unification of // superclass type constructor arguments. +// FIXME: The Requirement Machine also fails to minimize the signature of +// unifySuperclassTest(). rdar://90469643 + class Generic {} protocol P1 { @@ -21,6 +25,8 @@ func sameType(_: T.Type, _: T.Type) {} func takesGenericIntString(_: Generic.Type) {} +// CHECK-LABEL: .unifySuperclassTest@ +// CHECK-NEXT: Generic signature: func unifySuperclassTest(_: T) { sameType(T.A1.self, String.self) sameType(T.A2.self, Int.self) @@ -28,23 +34,23 @@ func unifySuperclassTest(_: T) { takesGenericIntString(T.X.self) } -// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> -// CHECK-NEXT: Rewrite system: { -// CHECK: - τ_0_0.[P2:X] => τ_0_0.[P1:X] -// CHECK: - τ_0_0.[P1:X].[superclass: Generic<τ_0_0.[P2:A2], String, τ_0_0.[P2:B2]>] => τ_0_0.[P1:X] -// CHECK: - τ_0_0.[P1:X].[superclass: Generic] => τ_0_0.[P1:X] -// CHECK: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1] -// CHECK: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2] -// CHECK: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1] -// CHECK: - τ_0_0.B2 => τ_0_0.[P1:B1] -// CHECK: } -// CHECK: Property map: { -// CHECK-NEXT: [P1] => { conforms_to: [P1] } -// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Generic] } -// CHECK-NEXT: [P2] => { conforms_to: [P2] } -// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] } -// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] } -// CHECK-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Generic] } -// CHECK-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] } -// CHECK-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] } -// CHECK-NEXT: } +// CHECK-RULE-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> +// CHECK-RULE-NEXT: Rewrite system: { +// CHECK-RULE: - τ_0_0.[P2:X] => τ_0_0.[P1:X] +// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic<τ_0_0.[P2:A2], String, τ_0_0.[P2:B2]>] => τ_0_0.[P1:X] +// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic] => τ_0_0.[P1:X] +// CHECK-RULE: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1] +// CHECK-RULE: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2] +// CHECK-RULE: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1] +// CHECK-RULE: - τ_0_0.B2 => τ_0_0.[P1:B1] +// CHECK-RULE: } +// CHECK-RULE: Property map: { +// CHECK-RULE-NEXT: [P1] => { conforms_to: [P1] } +// CHECK-RULE-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Generic] } +// CHECK-RULE-NEXT: [P2] => { conforms_to: [P2] } +// CHECK-RULE-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] } +// CHECK-RULE-NEXT: τ_0_0 => { conforms_to: [P1 P2] } +// CHECK-RULE-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Generic] } +// CHECK-RULE-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] } +// CHECK-RULE-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] } +// CHECK-RULE-NEXT: } diff --git a/test/Generics/unify_superclass_types_3.swift b/test/Generics/unify_superclass_types_3.swift index c09a7b62636bf..c41b5d5e21f06 100644 --- a/test/Generics/unify_superclass_types_3.swift +++ b/test/Generics/unify_superclass_types_3.swift @@ -1,8 +1,12 @@ -// RUN: %target-typecheck-verify-swift -dump-requirement-machine 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift -debug-generic-signatures -requirement-machine-inferred-signatures=off 2>&1 | %FileCheck %s +// RUN: %target-typecheck-verify-swift -dump-requirement-machine -requirement-machine-inferred-signatures=off 2>&1 | %FileCheck %s --check-prefix=CHECK-RULE // Note: The GSB fails this test, because it doesn't implement unification of // superclass type constructor arguments. +// FIXME: The Requirement Machine also fails to minimize the signature of +// unifySuperclassTest(). rdar://90469643 + class Generic {} class Derived : Generic {} @@ -23,6 +27,8 @@ func sameType(_: T.Type, _: T.Type) {} func takesDerivedString(_: Derived.Type) {} +// CHECK-LABEL: .unifySuperclassTest@ +// CHECK-NEXT: Generic signature: func unifySuperclassTest(_: T) { sameType(T.A1.self, String.self) sameType(T.A2.self, Int.self) @@ -30,25 +36,25 @@ func unifySuperclassTest(_: T) { takesDerivedString(T.X.self) } -// CHECK-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> -// CHECK-NEXT: Rewrite system: { -// CHECK: - [P1:X].[layout: _NativeClass] => [P1:X] -// CHECK: - [P2:X].[layout: _NativeClass] => [P2:X] -// CHECK: - τ_0_0.[P2:X] => τ_0_0.[P1:X] -// CHECK: - τ_0_0.[P1:X].[superclass: Generic] => τ_0_0.[P1:X] -// CHECK: - τ_0_0.[P1:X].[superclass: Generic] => τ_0_0.[P1:X] -// CHECK: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2] -// CHECK: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1] -// CHECK: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1] -// CHECK: - τ_0_0.B2 => τ_0_0.[P1:B1] -// CHECK: } -// CHECK: Property map: { -// CHECK-NEXT: [P1] => { conforms_to: [P1] } -// CHECK-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Derived<[P1:A1], [P1:B1]>] } -// CHECK-NEXT: [P2] => { conforms_to: [P2] } -// CHECK-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] } -// CHECK-NEXT: τ_0_0 => { conforms_to: [P1 P2] } -// CHECK-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] } -// CHECK-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] } -// CHECK-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] } -// CHECK-NEXT: } +// CHECK-RULE-LABEL: Requirement machine for <τ_0_0 where τ_0_0 : P1, τ_0_0 : P2> +// CHECK-RULE-NEXT: Rewrite system: { +// CHECK-RULE: - [P1:X].[layout: _NativeClass] => [P1:X] +// CHECK-RULE: - [P2:X].[layout: _NativeClass] => [P2:X] +// CHECK-RULE: - τ_0_0.[P2:X] => τ_0_0.[P1:X] +// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic] => τ_0_0.[P1:X] +// CHECK-RULE: - τ_0_0.[P1:X].[superclass: Generic] => τ_0_0.[P1:X] +// CHECK-RULE: - τ_0_0.[P2:A2].[concrete: Int] => τ_0_0.[P2:A2] +// CHECK-RULE: - τ_0_0.[P2:B2] => τ_0_0.[P1:B1] +// CHECK-RULE: - τ_0_0.[P1:A1].[concrete: String] => τ_0_0.[P1:A1] +// CHECK-RULE: - τ_0_0.B2 => τ_0_0.[P1:B1] +// CHECK-RULE: } +// CHECK-RULE: Property map: { +// CHECK-RULE-NEXT: [P1] => { conforms_to: [P1] } +// CHECK-RULE-NEXT: [P1:X] => { layout: _NativeClass superclass: [superclass: Derived<[P1:A1], [P1:B1]>] } +// CHECK-RULE-NEXT: [P2] => { conforms_to: [P2] } +// CHECK-RULE-NEXT: [P2:X] => { layout: _NativeClass superclass: [superclass: Generic<[P2:A2], String, [P2:B2]>] } +// CHECK-RULE-NEXT: τ_0_0 => { conforms_to: [P1 P2] } +// CHECK-RULE-NEXT: τ_0_0.[P1:X] => { layout: _NativeClass superclass: [superclass: Derived<τ_0_0.[P1:A1], τ_0_0.[P1:B1]>] } +// CHECK-RULE-NEXT: τ_0_0.[P2:A2] => { concrete_type: [concrete: Int] } +// CHECK-RULE-NEXT: τ_0_0.[P1:A1] => { concrete_type: [concrete: String] } +// CHECK-RULE-NEXT: } diff --git a/test/attr/accessibility.swift b/test/attr/accessibility.swift index fe21c45b375a5..e8d277cd76aae 100644 --- a/test/attr/accessibility.swift +++ b/test/attr/accessibility.swift @@ -207,31 +207,3 @@ extension GenericStruct where Param: InternalProto { public func foo() {} // expected-error {{cannot declare a public instance method in an extension with internal requirements}} {{3-9=internal}} } - -public class OuterClass { - class InnerClass {} -} - -public protocol PublicProto2 { - associatedtype T - associatedtype U -} - -// FIXME: With the current design, the below should not diagnose. -// -// However, it does, because we look at the bound decl in the -// TypeRepr first, and it happens to already be set. -// -// FIXME: Once we no longer do that, come up with another strategy -// to make the above diagnose. - -extension PublicProto2 where Self.T : OuterClass, Self.U == Self.T.InnerClass { - public func cannotBePublic() {} - // expected-error@-1 {{cannot declare a public instance method in an extension with internal requirements}} -} - -public extension OuterClass { - open convenience init(x: ()) { self.init() } - // expected-warning@-1 {{'open' modifier conflicts with extension's default access of 'public'}} - // expected-error@-2 {{only classes and overridable class members can be declared 'open'; use 'public'}} -} diff --git a/test/attr/accessibility_where_clause.swift b/test/attr/accessibility_where_clause.swift new file mode 100644 index 0000000000000..52f63c71f2886 --- /dev/null +++ b/test/attr/accessibility_where_clause.swift @@ -0,0 +1,32 @@ +// RUN: %target-typecheck-verify-swift -requirement-machine-inferred-signatures=off + +public class OuterClass { + class InnerClass {} +} + +public protocol PublicProto2 { + associatedtype T + associatedtype U +} + +// FIXME: With the current design, the below should not diagnose. +// +// However, it does, because we look at the bound decl in the +// TypeRepr first, and it happens to already be set. +// +// FIXME: Once we no longer do that, come up with another strategy +// to make the above diagnose. + +// FIXME: Get this working with the Requirement Machine, or decide that it should +// be unsupported: rdar://90469477 + +extension PublicProto2 where Self.T : OuterClass, Self.U == Self.T.InnerClass { + public func cannotBePublic() {} + // expected-error@-1 {{cannot declare a public instance method in an extension with internal requirements}} +} + +public extension OuterClass { + open convenience init(x: ()) { self.init() } + // expected-warning@-1 {{'open' modifier conflicts with extension's default access of 'public'}} + // expected-error@-2 {{only classes and overridable class members can be declared 'open'; use 'public'}} +} From 058190e2b9beb503690a8d399bb1e7919539e842 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Thu, 3 Mar 2022 15:54:30 -0500 Subject: [PATCH 205/242] Enable -requirement-machine-inferred-signatures=verify by default --- lib/Frontend/CompilerInvocation.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 46f020f6a6204..5ea2f23bbdf33 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -913,6 +913,7 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Args.hasArg(OPT_disable_subst_sil_function_types); Opts.RequirementMachineProtocolSignatures = RequirementMachineMode::Verify; + Opts.RequirementMachineInferredSignatures = RequirementMachineMode::Verify; Opts.RequirementMachineAbstractSignatures = RequirementMachineMode::Verify; if (auto A = Args.getLastArg(OPT_requirement_machine_protocol_signatures_EQ)) { From 2206b1c54d0fdc6677774e6198c1645ffabbdbae Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 18 Mar 2022 01:22:30 -0400 Subject: [PATCH 206/242] RequirementMachine: Split up equivalence classes with inferred concrete type I'll clean this up, comment it and generalize it to work with protocol requirement signatures soon. Landing this now to unblock the Linux corelibs-foundation build. Fixes rdar://problem/89791117. --- .../RequirementMachineRequests.cpp | 68 ++++++++++++++++++- .../split_concrete_equivalence_class.swift | 37 ++++++++++ 2 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 test/Generics/split_concrete_equivalence_class.swift diff --git a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp index 382b78493f647..2f14a20e4355f 100644 --- a/lib/AST/RequirementMachine/RequirementMachineRequests.cpp +++ b/lib/AST/RequirementMachine/RequirementMachineRequests.cpp @@ -243,6 +243,59 @@ static bool isCanonicalRequest(GenericSignature baseSignature, return true; } +/// Hack for GenericSignatureBuilder compatibility. We might end up with a +/// same-type requirement between type parameters where one of them has an +/// implied concrete type requirement. In this case, split it up into two +/// concrete type requirements. +static bool shouldSplitConcreteEquivalenceClass(Requirement req, + GenericSignature sig) { + return (req.getKind() == RequirementKind::SameType && + req.getSecondType()->isTypeParameter() && + sig->isConcreteType(req.getSecondType())); +} + +static bool shouldSplitConcreteEquivalenceClasses(GenericSignature sig) { + for (auto req : sig.getRequirements()) { + if (shouldSplitConcreteEquivalenceClass(req, sig)) + return true; + } + + return false; +} + +static GenericSignature splitConcreteEquivalenceClasses( + GenericSignature sig, ASTContext &ctx) { + SmallVector reqs; + + for (auto req : sig.getRequirements()) { + if (shouldSplitConcreteEquivalenceClass(req, sig)) { + auto canType = sig->getSugaredType( + sig.getCanonicalTypeInContext( + req.getSecondType())); + + reqs.emplace_back(RequirementKind::SameType, + req.getFirstType(), + canType); + reqs.emplace_back(RequirementKind::SameType, + req.getSecondType(), + canType); + } else { + reqs.push_back(req); + } + } + + SmallVector genericParams; + genericParams.append(sig.getGenericParams().begin(), + sig.getGenericParams().end()); + + return evaluateOrDefault( + ctx.evaluator, + AbstractGenericSignatureRequestRQM{ + /*baseSignature=*/nullptr, + genericParams, reqs}, + GenericSignatureWithError()).getPointer(); +} + GenericSignatureWithError AbstractGenericSignatureRequestRQM::evaluate( Evaluator &evaluator, @@ -391,8 +444,13 @@ AbstractGenericSignatureRequestRQM::evaluate( auto result = GenericSignature::get(genericParams, minimalRequirements); auto errorFlags = machine->getErrors(); - if (!errorFlags) + if (!errorFlags) { + if (shouldSplitConcreteEquivalenceClasses(result)) + result = splitConcreteEquivalenceClasses(result, ctx); + + // Check invariants. result.verify(); + } return GenericSignatureWithError(result, errorFlags); } @@ -543,9 +601,13 @@ InferredGenericSignatureRequestRQM::evaluate( // FIXME: Handle allowConcreteGenericParams - // Check invariants. - if (!errorFlags) + if (!errorFlags) { + if (shouldSplitConcreteEquivalenceClasses(result)) + result = splitConcreteEquivalenceClasses(result, ctx); + + // Check invariants. result.verify(); + } return GenericSignatureWithError(result, errorFlags); } diff --git a/test/Generics/split_concrete_equivalence_class.swift b/test/Generics/split_concrete_equivalence_class.swift new file mode 100644 index 0000000000000..646df21199071 --- /dev/null +++ b/test/Generics/split_concrete_equivalence_class.swift @@ -0,0 +1,37 @@ +// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-inferred-signatures=on 2>&1 | %FileCheck %s + +// CHECK-LABEL: split_concrete_equivalence_class.(file).f01@ +// CHECK-NEXT: Generic signature: +func f01(_: C, _: R) where C.SubSequence == Substring, C.Element == R.Element {} + +// CHECK-LABEL: split_concrete_equivalence_class.(file).f02@ +// CHECK-NEXT: Generic signature: +func f02(_: C, _: R) where R.SubSequence == Substring, C.Element == R.Element {} + +// CHECK-LABEL: split_concrete_equivalence_class.(file).f03@ +// CHECK-NEXT: Generic signature: +func f03(_: C, _: R) where C.SubSequence == Substring, C.Element == R.Element, C.Element == Character {} + +// CHECK-LABEL: split_concrete_equivalence_class.(file).f04@ +// CHECK-NEXT: Generic signature: +func f04(_: C, _: R) where R.SubSequence == Substring, C.Element == R.Element, C.Element == Character {} + +// CHECK-LABEL: split_concrete_equivalence_class.(file).f05@ +// CHECK-NEXT: Generic signature: +func f05(_: C, _: R) where C.SubSequence == Substring, C.Element == R.Element, R.Element == Character {} + +// CHECK-LABEL: split_concrete_equivalence_class.(file).f06@ +// CHECK-NEXT: Generic signature: +func f06(_: C, _: R) where R.SubSequence == Substring, C.Element == R.Element, R.Element == Character {} + +// CHECK-LABEL: split_concrete_equivalence_class.(file).f07@ +// CHECK-NEXT: Generic signature: +func f07(_: C, _: R) where C.SubSequence == Substring, R.SubSequence == Substring, C.Element == R.Element {} + +// CHECK-LABEL: split_concrete_equivalence_class.(file).f08@ +// CHECK-NEXT: Generic signature: +func f08(_: C, _: R) where C.SubSequence == Substring, R.SubSequence == Substring, C.Element == R.Element, C.Element == Character {} + +// CHECK-LABEL: split_concrete_equivalence_class.(file).f09@ +// CHECK-NEXT: Generic signature: +func f09(_: C, _: R) where C.SubSequence == Substring, R.SubSequence == Substring, C.Element == R.Element, R.Element == Character {} From a8cec01c6f21a71d6f617fc560360cf87871dfaf Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 18 Mar 2022 08:11:34 +0100 Subject: [PATCH 207/242] [CodeCompletion] Exit early in applyResultBuilderBodyTransform after reporing solutions to code completion --- lib/Sema/BuilderTransform.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Sema/BuilderTransform.cpp b/lib/Sema/BuilderTransform.cpp index 9016862e26ca8..17b1d1d2b8d5c 100644 --- a/lib/Sema/BuilderTransform.cpp +++ b/lib/Sema/BuilderTransform.cpp @@ -1744,6 +1744,7 @@ Optional TypeChecker::applyResultBuilderBodyTransform( for (const auto &solution : solutions) { cs.getASTContext().CompletionCallback->sawSolution(solution); } + return nullptr; } if (solvingFailed || solutions.size() != 1) { From f7923ee6190d0d05af411728edcc67087f6d8ca1 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 18 Mar 2022 08:57:19 +0100 Subject: [PATCH 208/242] [build-presets] Disable SourceKit-LSP tests for SwiftPM Linux CI jobs SourceKit-LSP tests are flaky on Linux, disable them until we have fixed the root cause. Resolves rdar://89359439 --- utils/build-presets.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 71be70733273d..fab8af0f9e760 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -1705,6 +1705,8 @@ mixin-preset=mixin_swiftpm_linux_platform skip-test-llbuild skip-test-swiftpm +# SourceKit-LSP tests are flaky on Linux, disable them until we have fixed the root cause - rdar://90437872 +skip-test-sourcekit-lsp #===------------------------------------------------------------------------===# From ddf0965d3f2040a8c141e23bba251f3240de1019 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 14 Mar 2022 19:51:34 -0700 Subject: [PATCH 209/242] Rewrite ClosureScopeAnalysis for generality. Handle recursive non-escaping local functions. Previously, it was thought that recursion would force a closure to be escaping. This is not necessarilly true. Update AccessEnforcementSelection to conservatively handle closure cycles. Fixes rdar://88726092 (Compiler hangs when building) --- include/swift/SIL/ApplySite.h | 4 + .../SILOptimizer/Analysis/ClosureScope.h | 100 ++-- .../Analysis/AccessSummaryAnalysis.cpp | 7 +- lib/SILOptimizer/Analysis/ClosureScope.cpp | 451 +++++++++++++----- .../Mandatory/AccessEnforcementSelection.cpp | 55 ++- .../access_enforcement_selection.swift | 25 + .../SILOptimizer/closure_scope_analysis.swift | 77 +++ 7 files changed, 510 insertions(+), 209 deletions(-) create mode 100644 test/SILOptimizer/closure_scope_analysis.swift diff --git a/include/swift/SIL/ApplySite.h b/include/swift/SIL/ApplySite.h index 14a8abb965c0e..5bc9b004b5084 100644 --- a/include/swift/SIL/ApplySite.h +++ b/include/swift/SIL/ApplySite.h @@ -169,6 +169,10 @@ class ApplySite { FOREACH_IMPL_RETURN(getCalleeFunction()); } + bool isCalleeDynamicallyReplaceable() const { + FOREACH_IMPL_RETURN(isCalleeDynamicallyReplaceable()); + } + /// Return the referenced function if the callee is a function_ref /// instruction. SILFunction *getReferencedFunctionOrNull() const { diff --git a/include/swift/SILOptimizer/Analysis/ClosureScope.h b/include/swift/SILOptimizer/Analysis/ClosureScope.h index 7c428acd5c522..99cfcd47412c1 100644 --- a/include/swift/SILOptimizer/Analysis/ClosureScope.h +++ b/include/swift/SILOptimizer/Analysis/ClosureScope.h @@ -52,6 +52,7 @@ #include "swift/Basic/BlotSetVector.h" #include "swift/SIL/SILFunction.h" +#include "swift/SIL/SILModule.h" #include "swift/SILOptimizer/Analysis/Analysis.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/iterator.h" @@ -80,40 +81,15 @@ inline bool isNonEscapingClosure(CanSILFunctionType funcTy) { return llvm::any_of(funcTy->getParameters(), isInoutAliasable); } -class ClosureScopeData; +class ClosureGraph; class ClosureScopeAnalysis : public SILAnalysis { - friend class ClosureScopeData; - - // Get a closure's scope function from its index. This functor is compatible - // with OptionalTransformRange. Unfortunately it exposes the internals of the - // analysis data. - struct IndexLookupFunc { - // A reference to all closure parent scopes ordered by their index. - const std::vector &indexedScopes; - - IndexLookupFunc(const std::vector &indexedScopes) - : indexedScopes(indexedScopes) {} - - Optional operator()(int idx) const { - if (auto funcPtr = indexedScopes[idx]) { - return funcPtr; - } - return None; - } - }; - using IndexRange = iterator_range; - -public: - // A range of SILFunction scopes converted from their scope indices and - // filtered to remove any erased functions. - using ScopeRange = OptionalTransformRange; + friend class ClosureGraph; -private: SILModule *M; // The analysis data. nullptr if it has never been computed. - std::unique_ptr scopeData; + std::unique_ptr scopeGraph; public: ClosureScopeAnalysis(SILModule *M); @@ -125,13 +101,20 @@ class ClosureScopeAnalysis : public SILAnalysis { SILModule *getModule() const { return M; } - // Return true if the given function is the parent scope for any closures. - bool isClosureScope(SILFunction *scopeFunc); + /// Visit the parent scopes of \p closure if it has any. If \p visitor returns + /// false, exit early and return false. Otherwise return true. + bool visitClosureScopes(SILFunction *closure, + std::function visitor); - // Return a range of scopes for the given closure. The elements of the - // returned range have type `SILFunction *` and are non-null. Returns an - // empty range for a SILFunction that is not a closure or is a dead closure. - ScopeRange getClosureScopes(SILFunction *closureFunc); + /// Visit the closures directly referenced by \p scopeFunc. + bool visitClosures(SILFunction *scopeFunc, + std::function visitor); + + /// Return true if this function is a reachable closure. + bool isReachableClosure(SILFunction *function) { + // This visitor returns false immediately on the first scope. + return !visitClosureScopes(function, [](SILFunction *) { return false; }); + } /// Invalidate all information in this analysis. virtual void invalidate() override; @@ -159,25 +142,50 @@ class ClosureScopeAnalysis : public SILAnalysis { } protected: - ClosureScopeData *getOrComputeScopeData(); + ClosureScopeAnalysis(const ClosureScopeAnalysis &) = delete; + ClosureScopeAnalysis &operator=(const ClosureScopeAnalysis &) = delete; + + ClosureGraph *getOrComputeGraph(); }; -// ClosureScopeAnalysis utility for visiting functions top down in closure scope -// order. -class TopDownClosureFunctionOrder { - ClosureScopeAnalysis *CSA; +// ClosureScopeAnalysis utility for visiting functions top-down or bottom-up in +// closure scope order. +class ClosureFunctionOrder { + class ClosureDFS; + friend class ClosureDFS; + + ClosureScopeAnalysis *csa; - llvm::SmallSet visited; + // All functions in this module in top-down order (RPO) following the closure + // scope graph. Functions that define a closure occur before the closure. + std::vector topDownFunctions; - BlotSetVector closureWorklist; + // If the closure scope graph has any cycles, record each function at the + // head of a cycle. This does not include all the functions in the + // strongly-connected component. This is extremely rare. It is always a local + // function that refers to itself either directly or indirectly. + SmallPtrSet closureCycleHeads; public: - TopDownClosureFunctionOrder(ClosureScopeAnalysis *CSA) : CSA(CSA) {} + ClosureFunctionOrder(ClosureScopeAnalysis *csa) : csa(csa) {} - // Visit all functions in a module, visiting each closure scope function - // before - // the closure function itself. - void visitFunctions(llvm::function_ref visitor); + void compute(); + + ArrayRef getTopDownFunctions() const { + assert(!topDownFunctions.empty() + || llvm::empty(csa->getModule()->getFunctions())); + return topDownFunctions; + } + + bool isHeadOfClosureCycle(SILFunction *function) const { + return closureCycleHeads.contains(function); + } + + SWIFT_ASSERT_ONLY_DECL(void dump()); + +protected: + ClosureFunctionOrder(const ClosureFunctionOrder &) = delete; + ClosureFunctionOrder &operator=(const ClosureFunctionOrder &) = delete; }; } // end namespace swift diff --git a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp index c642e76384a39..2fb39e750cbf8 100644 --- a/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp +++ b/lib/SILOptimizer/Analysis/AccessSummaryAnalysis.cpp @@ -162,6 +162,9 @@ static bool hasExpectedUsesOfNoEscapePartialApply(Operand *partialApplyUse) { if (user->getModule().getASTContext().hadError()) return true; + if (isIncidentalUse(user)) + return true; + // It is fine to call the partial apply switch (user->getKind()) { case SILInstructionKind::ApplyInst: @@ -206,10 +209,6 @@ static bool hasExpectedUsesOfNoEscapePartialApply(Operand *partialApplyUse) { return llvm::all_of(cast(user)->getUses(), hasExpectedUsesOfNoEscapePartialApply); - // End borrow is always ok. - case SILInstructionKind::EndBorrowInst: - return true; - case SILInstructionKind::IsEscapingClosureInst: case SILInstructionKind::StoreInst: case SILInstructionKind::DestroyValueInst: diff --git a/lib/SILOptimizer/Analysis/ClosureScope.cpp b/lib/SILOptimizer/Analysis/ClosureScope.cpp index fbec6449a437c..1268982dfaaca 100644 --- a/lib/SILOptimizer/Analysis/ClosureScope.cpp +++ b/lib/SILOptimizer/Analysis/ClosureScope.cpp @@ -12,202 +12,391 @@ /// /// Implementation of ClosureScopeAnalysis. /// +/// The "scope" of a closure is the function body that refers to the closure. +/// //===----------------------------------------------------------------------===// #define DEBUG_TYPE "closure-scope" -#include "swift/SIL/SILModule.h" #include "swift/SILOptimizer/Analysis/ClosureScope.h" +#include "swift/SIL/ApplySite.h" +#include "swift/SIL/SILModule.h" +#include "llvm/ADT/iterator.h" namespace swift { -class ClosureScopeData { - using IndexRange = ClosureScopeAnalysis::IndexRange; - using IndexLookupFunc = ClosureScopeAnalysis::IndexLookupFunc; - using ScopeRange = ClosureScopeAnalysis::ScopeRange; +// A function is a node in this graph if it either refers to a closure or is +// itself a closure. +// +// The graph is represented with two vectors. One for forward edges from "scope +// functions" to closures. Another with backward edges from closures to +// "scope functions". +class ClosureGraph { + struct Edge { + SILFunction *from; + SILFunction *to; + + bool operator<(const Edge &other) const { + unsigned from1 = from->getIndex(); + unsigned from2 = other.from->getIndex(); + if (from1 != from2) + return from1 < from2; + + return to->getIndex() < other.to->getIndex(); + } + }; + + using EdgePos = std::vector::iterator; + using EdgeRange = llvm::iterator_range; -private: - // Map an index to each SILFunction with a closure scope. - std::vector indexedScopes; + struct NodeIterator + : llvm::iterator_adaptor_base< + NodeIterator, EdgePos, std::random_access_iterator_tag, + SILFunction *, ptrdiff_t, SILFunction *, SILFunction *> { - // Map each SILFunction with a closure scope to an index. - llvm::DenseMap scopeToIndexMap; + NodeIterator() = default; - // A list of all indices for the SILFunctions that partially apply this - // closure. The indices index into the `indexedScopes` vector. If the indexed - // scope is nullptr, then that function has been deleted. - using ClosureScopes = llvm::SmallVector; + explicit NodeIterator(EdgePos pos) : iterator_adaptor_base(pos) {} - // Map each closure to its parent scopes. - llvm::DenseMap closureToScopesMap; + EdgePos edgeIter() const { return I; } -public: - void reset() { - indexedScopes.clear(); - scopeToIndexMap.clear(); - closureToScopesMap.clear(); - } + SILFunction *operator*() const { return I->from; } + }; - void erase(SILFunction *F) { - // If this function is a mapped closure scope, remove it, leaving a nullptr - // sentinel. - auto indexPos = scopeToIndexMap.find(F); - if (indexPos != scopeToIndexMap.end()) { - indexedScopes[indexPos->second] = nullptr; - scopeToIndexMap.erase(F); + // Compare the node identified by the from index of \p edge with the node + // identified by \p functionIndex. + struct NodeCompare { + bool operator()(SILFunction *node1, SILFunction *node2) { + return node1->getIndex() < node2->getIndex(); } - // If this function is a closure, remove it. - closureToScopesMap.erase(F); - } + }; + + // A set of edges in the relational mapping from a function to the set of + // nonescaping closures that are referenced within its body. + // + // from: parent function + // to: nonescaping child closure + std::vector functionToClosures; + + // Map every nonescaping closure to the parent function that refers to it. + // + // from: nonescaping child closure + // to: parent function + // + // A closure almost always has a single parent. A local function, however, may + // be referenced recursively, even within a different closure. Notice that + // capturedInt is captured from the outer-most function, but is passed down + // + // func localFunc(b: Apply) { + // capturedInt += 1 + // let closure = { (c: Apply) in + // c.apply(localFunc) + // } + // b.apply(closure) + // } + // a.apply(localFunc) + std::vector closureToFunctions; + +public: + ClosureGraph() = default; - // Record all closure scopes in this module. + /// Visit the parent scopes of \p closure if it has any. If \p visitor returns + /// false, exit early and return false. Otherwise return true. + bool visitClosureScopes(SILFunction *closure, + std::function visitor); + + /// Visit the closures directly referenced by \p scopeFunc. + bool visitClosures(SILFunction *scopeFunc, + std::function visitor); + + /// Called when a \p function is removed from this module. + void erase(SILFunction *function); + + /// Record all closure scopes in this module. void compute(SILModule *M); - bool isClosureScope(SILFunction *F) { return scopeToIndexMap.count(F); } - - // Return a range of scopes for the given closure. The elements of the - // returned range have type `SILFunction *` and are non-null. Return an empty - // range for a SILFunction that is not a closure or is a dead closure. - ScopeRange getClosureScopes(SILFunction *ClosureF) { - IndexRange indexRange(nullptr, nullptr); - auto closureScopesPos = closureToScopesMap.find(ClosureF); - if (closureScopesPos != closureToScopesMap.end()) { - auto &indexedScopes = closureScopesPos->second; - indexRange = IndexRange(indexedScopes.begin(), indexedScopes.end()); +protected: + ClosureGraph(const ClosureGraph &) = delete; + ClosureGraph &operator=(const ClosureGraph &) = delete; + + EdgeRange getEdgeRange(SILFunction *node, std::vector &edges) { + auto it = std::lower_bound(NodeIterator(edges.begin()), + NodeIterator(edges.end()), + node, NodeCompare()); + auto next = it.edgeIter(); + for (auto end = edges.end(); next != end; ++next) { + if (next->from != node) + break; } - return makeOptionalTransformRange(indexRange, - IndexLookupFunc(indexedScopes)); + return EdgeRange(it.edgeIter(), next); + } + + EdgeRange getFunctionToClosureEdges(SILFunction *scopeFunc) { + return getEdgeRange(scopeFunc, functionToClosures); } - void recordScope(PartialApplyInst *PAI) { - // Only track scopes of non-escaping closures. - auto closureTy = PAI->getCallee()->getType().castTo(); - // FIXME: isCalleeDynamicallyReplaceable should not be true but can today - // because local functions can be marked dynamic. - if (!isNonEscapingClosure(closureTy) || - PAI->isCalleeDynamicallyReplaceable()) - return; + EdgeRange getClosureToFunctionEdges(SILFunction *closure) { + return getEdgeRange(closure, closureToFunctions); + } - auto closureFunc = PAI->getCalleeFunction(); - assert(closureFunc && "non-escaping closure needs a direct partial_apply."); + void recordScope(ApplySite apply); - auto scopeFunc = PAI->getFunction(); - int scopeIdx = lookupScopeIndex(scopeFunc); + void finalize(); - // Passes may assume that a deserialized function can only refer to - // deserialized closures. For example, AccessEnforcementSelection skips - // deserialized functions but assumes all a closure's parent scope have been - // processed. - assert(scopeFunc->wasDeserializedCanonical() - == closureFunc->wasDeserializedCanonical() && - "A closure cannot be serialized in a different module than its " - "parent context"); + SWIFT_ASSERT_ONLY_DECL(void dump()); +}; - auto &indices = closureToScopesMap[closureFunc]; - if (std::find(indices.begin(), indices.end(), scopeIdx) != indices.end()) - return; +bool ClosureGraph::visitClosureScopes( + SILFunction *closure, std::function visitor) { + for (ClosureGraph::Edge &edge : getClosureToFunctionEdges(closure)) { + if (!visitor(edge.to)) + return false; + } + return true; +} - indices.push_back(scopeIdx); +bool ClosureGraph::visitClosures( + SILFunction *scopeFunc, std::function visitor) { + for (ClosureGraph::Edge &edge : getFunctionToClosureEdges(scopeFunc)) { + if (!visitor(edge.to)) + return false; } + return true; +} -protected: - int lookupScopeIndex(SILFunction *scopeFunc) { - auto indexPos = scopeToIndexMap.find(scopeFunc); - if (indexPos != scopeToIndexMap.end()) - return indexPos->second; - - int scopeIdx = indexedScopes.size(); - scopeToIndexMap[scopeFunc] = scopeIdx; - indexedScopes.push_back(scopeFunc); - return scopeIdx; +void ClosureGraph::erase(SILFunction *function) { + struct RefersToFunction { + SILFunction *function; + + bool operator()(const Edge &edge) { + return edge.from == function || edge.to == function; + } + }; + llvm::erase_if(functionToClosures, RefersToFunction{function}); + llvm::erase_if(closureToFunctions, RefersToFunction{function}); +} + +// Handle both partial_apply and directly applied closures of the form: +// %f = function_ref @... : $(...inout_aliasable...) -> ... +// apply %f(...) +void ClosureGraph::recordScope(ApplySite apply) { + // Only track scopes of non-escaping closures. + auto closureTy = apply.getCallee()->getType().castTo(); + + // FIXME: isCalleeDynamicallyReplaceable should not be true but can today + // because local functions can be marked dynamic. + if (!isNonEscapingClosure(closureTy) + || apply.isCalleeDynamicallyReplaceable()) { + return; } -}; + auto closureFunc = apply.getCalleeFunction(); + assert(closureFunc && "non-escaping closure needs a direct partial_apply."); + + auto scopeFunc = apply.getFunction(); + // Passes may assume that a deserialized function can only refer to + // deserialized closures. For example, AccessEnforcementSelection skips + // deserialized functions but assumes all a closure's parent scope have been + // processed. + assert(scopeFunc->wasDeserializedCanonical() == + closureFunc->wasDeserializedCanonical() && + "A closure cannot be serialized in a different module than its " + "parent context"); + + functionToClosures.push_back({scopeFunc, closureFunc}); + closureToFunctions.push_back({closureFunc, scopeFunc}); +} -void ClosureScopeData::compute(SILModule *M) { +void ClosureGraph::finalize() { + llvm::stable_sort(functionToClosures); + llvm::stable_sort(closureToFunctions); + + LLVM_DEBUG(dump()); +} + +#ifndef NDEBUG +static void dumpFunctionName(SILFunction *function) { + llvm::dbgs() << Demangle::demangleSymbolAsString( + function->getName(), + Demangle::DemangleOptions::SimplifiedUIDemangleOptions()) + << " '" << function->getName() << "'\n"; +} + +void ClosureGraph::dump() { + llvm::dbgs() << "\n"; + SILFunction *currentFunc = nullptr; + for (auto &edge : functionToClosures) { + auto *scopeFunc = edge.from; + if (currentFunc != scopeFunc) { + currentFunc = scopeFunc; + llvm::dbgs() << "SCOPE: "; + dumpFunctionName(scopeFunc); + } + llvm::dbgs() << " CLOSURE: "; + dumpFunctionName(edge.to); + } + currentFunc = nullptr; + for (auto &edge : closureToFunctions) { + auto *closure = edge.from; + if (currentFunc != closure) { + currentFunc = closure; + llvm::dbgs() << "CLOSURE: "; + dumpFunctionName(closure); + } + llvm::dbgs() << " SCOPE: "; + dumpFunctionName(edge.to); + } +} +#endif + +void ClosureGraph::compute(SILModule *M) { for (auto &F : *M) { for (auto &BB : F) { for (auto &I : BB) { - if (auto *PAI = dyn_cast(&I)) { - recordScope(PAI); + if (auto apply = ApplySite::isa(&I)) { + recordScope(apply); } } } } + finalize(); } ClosureScopeAnalysis::ClosureScopeAnalysis(SILModule *M) - : SILAnalysis(SILAnalysisKind::ClosureScope), M(M), scopeData(nullptr) {} + : SILAnalysis(SILAnalysisKind::ClosureScope), M(M), scopeGraph(nullptr) {} ClosureScopeAnalysis::~ClosureScopeAnalysis() = default; -bool ClosureScopeAnalysis::isClosureScope(SILFunction *scopeFunc) { - return getOrComputeScopeData()->isClosureScope(scopeFunc); +bool ClosureScopeAnalysis::visitClosureScopes( + SILFunction *closure, std::function visitor) { + return getOrComputeGraph()->visitClosureScopes(closure, visitor); } -ClosureScopeAnalysis::ScopeRange -ClosureScopeAnalysis::getClosureScopes(SILFunction *closureFunc) { - return getOrComputeScopeData()->getClosureScopes(closureFunc); +bool ClosureScopeAnalysis::visitClosures( + SILFunction *scopeFunc, std::function visitor) { + return getOrComputeGraph()->visitClosures(scopeFunc, visitor); } void ClosureScopeAnalysis::invalidate() { - if (scopeData) scopeData->reset(); + scopeGraph.reset(); } void ClosureScopeAnalysis::notifyWillDeleteFunction(SILFunction *F) { - if (scopeData) scopeData->erase(F); + if (scopeGraph) + scopeGraph->erase(F); } -ClosureScopeData *ClosureScopeAnalysis::getOrComputeScopeData() { - if (!scopeData) { - scopeData = std::make_unique(); - scopeData->compute(M); +ClosureGraph *ClosureScopeAnalysis::getOrComputeGraph() { + if (!scopeGraph) { + scopeGraph = std::make_unique(); + scopeGraph->compute(M); } - return scopeData.get(); + return scopeGraph.get(); } SILAnalysis *createClosureScopeAnalysis(SILModule *M) { return new ClosureScopeAnalysis(M); } -void TopDownClosureFunctionOrder::visitFunctions( - llvm::function_ref visitor) { - auto markVisited = [&](SILFunction *F) { - bool visitOnce = visited.insert(F).second; - assert(visitOnce); - (void)visitOnce; - }; - auto allScopesVisited = [&](SILFunction *closureF) { - return llvm::all_of(CSA->getClosureScopes(closureF), - [this](SILFunction *F) { return visited.count(F); }); +class ClosureFunctionOrder::ClosureDFS { + ClosureFunctionOrder &functionOrder; + + llvm::SmallBitVector visited; + llvm::SmallBitVector finished; + + SmallVector postorderClosures; + +public: + ClosureDFS(ClosureFunctionOrder &functionOrder) + : functionOrder(functionOrder), + visited(functionOrder.csa->getModule()->getNumFunctionIndices()), + finished(functionOrder.csa->getModule()->getNumFunctionIndices()) + {} + + bool isVisited(SILFunction *function) const { + return visited.test(function->getIndex()); + } + + void performDFS(SILFunction *root) { + postorderClosures.clear(); + + recursiveDFS(root); + + // Closures are discovered in postorder, bottom-up. + // Reverse-append them onto the top-down function list. + llvm::append_range(functionOrder.topDownFunctions, + llvm::reverse(postorderClosures)); + } + +protected: + void recursiveDFS(SILFunction *function) { + unsigned index = function->getIndex(); + if (visited.test(index)) { + if (!finished.test(index)) { + // Cycle in the closure graph. + functionOrder.closureCycleHeads.insert(function); + } + return; + } + visited.set(index); + + functionOrder.csa->visitClosures(function, [this](SILFunction *closure) { + recursiveDFS(closure); + return true; + }); + finished.set(index); + postorderClosures.push_back(function); }; - for (auto &F : *CSA->getModule()) { - if (!allScopesVisited(&F)) { - closureWorklist.insert(&F); +}; + +void ClosureFunctionOrder::compute() { + auto *module = csa->getModule(); + + assert(topDownFunctions.empty() && closureCycleHeads.empty() && + "attempting to recompute"); + topDownFunctions.reserve(module->getNumFunctionIndices()); + + ClosureDFS dfs(*this); + SmallVector closureWorklist; + + unsigned numFunctions = 0; + for (auto &function : module->getFunctionList()) { + ++numFunctions; + if (dfs.isVisited(&function)) + continue; + + if (csa->isReachableClosure(&function)) { + // This is a closure, reachable from some other function. Reaching this + // point is rare, because closures typically follow their parent in the + // function list. + // + // A closure at the head of a cycle might only be reachable from other + // closures. So use a worklist to visit them once more as DFS roots after + // finishing DFS from all acyclic functions. + closureWorklist.push_back(&function); continue; } - markVisited(&F); - visitor(&F); + dfs.performDFS(&function); } - unsigned numClosures = closureWorklist.size(); - while (numClosures) { - unsigned prevNumClosures = numClosures; - for (auto &closureNode : closureWorklist) { - // skip erased closures. - if (!closureNode) - continue; - - auto closureF = closureNode.getValue(); - if (!allScopesVisited(closureF)) - continue; - - markVisited(closureF); - visitor(closureF); - closureWorklist.erase(closureF); - --numClosures; - } - assert(numClosures < prevNumClosures && "Cyclic closures scopes"); - (void)prevNumClosures; + // Revisit closures in forward order. DFS will immediately return except in + // the unlikely event that this is an orphaned closure. + for (auto *closure : closureWorklist) { + dfs.performDFS(closure); + } + LLVM_DEBUG(dump()); + assert(numFunctions == topDownFunctions.size() && "DFS missed a function"); +} + +#ifndef NDEBUG +void ClosureFunctionOrder::dump() { + llvm::dbgs() << "\nRPO function order:\n"; + for (auto *function : getTopDownFunctions()) { + llvm::dbgs() << "[" << function->getIndex() << "] "; + if (isHeadOfClosureCycle(function)) + llvm::dbgs() << "CYCLE HEAD: "; + + dumpFunctionName(function); } } +#endif } // namespace swift diff --git a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp index 23b9c2cf1a18a..7e395bfbe6767 100644 --- a/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp +++ b/lib/SILOptimizer/Mandatory/AccessEnforcementSelection.cpp @@ -32,6 +32,7 @@ //===----------------------------------------------------------------------===// #define DEBUG_TYPE "access-enforcement-selection" +#include "swift/Basic/Defer.h" #include "swift/SIL/ApplySite.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILFunction.h" @@ -98,12 +99,16 @@ raw_ostream &operator<<(raw_ostream &os, const AddressCapture &capture) { // For each non-escaping closure, record the indices of arguments that // require dynamic enforcement. class DynamicCaptures { + const ClosureFunctionOrder &closureOrder; + + // This only maps functions that have at least one inout_aliasable argument. llvm::DenseMap> dynamicCaptureMap; DynamicCaptures(DynamicCaptures &) = delete; public: - DynamicCaptures() = default; + DynamicCaptures(const ClosureFunctionOrder &closureOrder): + closureOrder(closureOrder) {} void recordCapture(AddressCapture capture) { LLVM_DEBUG(llvm::dbgs() << "Dynamic Capture: " << capture); @@ -121,6 +126,11 @@ class DynamicCaptures { } bool isDynamic(SILFunctionArgument *arg) const { + // If the current function is a local function that directly or indirectly + // refers to itself, then conservatively assume dynamic enforcement. + if (closureOrder.isHeadOfClosureCycle(arg->getFunction())) + return true; + auto pos = dynamicCaptureMap.find(arg->getFunction()); if (pos == dynamicCaptureMap.end()) return false; @@ -576,16 +586,15 @@ struct SourceAccess { /// /// TODO: Make this a "ClosureTransform". See the file-level comments above. class AccessEnforcementSelection : public SILModuleTransform { - // Reference back to the known dynamically enforced non-escaping closure + // Track the known dynamically enforced non-escaping closure // arguments in this module. Parent scopes are processed before the closures // they reference. - DynamicCaptures dynamicCaptures; + std::unique_ptr dynamicCaptures; #ifndef NDEBUG // Per-function book-keeping to verify that a box is processed before all of // its accesses and captures are seen. llvm::DenseSet handledBoxes; - llvm::DenseSet visited; #endif public: @@ -601,35 +610,25 @@ class AccessEnforcementSelection : public SILModuleTransform { void AccessEnforcementSelection::run() { auto *CSA = getAnalysis(); - TopDownClosureFunctionOrder closureOrder(CSA); - closureOrder.visitFunctions( - [this](SILFunction *F) { this->processFunction(F); }); + ClosureFunctionOrder closureOrder(CSA); + closureOrder.compute(); + + dynamicCaptures = std::make_unique(closureOrder); + SWIFT_DEFER { dynamicCaptures.release(); }; + + for (SILFunction *function : closureOrder.getTopDownFunctions()) { + this->processFunction(function); + } } -void AccessEnforcementSelection::processFunction(SILFunction *F) { +void AccessEnforcementSelection:: +processFunction(SILFunction *F) { if (F->isExternalDeclaration()) return; LLVM_DEBUG(llvm::dbgs() << "Access Enforcement Selection in " << F->getName() << "\n"); - // This ModuleTransform needs to analyze closures and their parent scopes in - // the same pass, and the parent needs to be analyzed before the closure. -#ifndef NDEBUG - auto *CSA = getAnalysis(); - if (isNonEscapingClosure(F->getLoweredFunctionType())) { - for (auto *scopeF : CSA->getClosureScopes(F)) { - LLVM_DEBUG(llvm::dbgs() << " Parent scope: " << scopeF->getName() - << "\n"); - assert(visited.count(scopeF)); - // Closures must be defined in the same module as their parent scope. - assert(scopeF->wasDeserializedCanonical() - == F->wasDeserializedCanonical()); - } - } - visited.insert(F); -#endif - // Deserialized functions, which have been mandatory inlined, no longer meet // the structural requirements on access markers required by this pass. if (F->wasDeserializedCanonical()) @@ -646,7 +645,7 @@ void AccessEnforcementSelection::processFunction(SILFunction *F) { // may still have captures that require dynamic enforcement because the // box has escaped prior to the capture. if (auto box = dyn_cast(inst)) { - SelectEnforcement(dynamicCaptures, box).run(); + SelectEnforcement(*dynamicCaptures, box).run(); assert(handledBoxes.insert(box).second); } else if (auto access = dyn_cast(inst)) @@ -703,7 +702,7 @@ SourceAccess AccessEnforcementSelection::getSourceAccess(SILValue address) { return SourceAccess::getStaticAccess(); case SILArgumentConvention::Indirect_InoutAliasable: - if (dynamicCaptures.isDynamic(arg)) + if (dynamicCaptures->isDynamic(arg)) return SourceAccess::getDynamicAccess(); return SourceAccess::getStaticAccess(); @@ -754,7 +753,7 @@ void AccessEnforcementSelection::handleApply(ApplySite apply) { // there's no need to track it. break; case SourceAccess::DynamicAccess: { - dynamicCaptures.recordCapture(capture); + dynamicCaptures->recordCapture(capture); break; } case SourceAccess::BoxAccess: diff --git a/test/SILOptimizer/access_enforcement_selection.swift b/test/SILOptimizer/access_enforcement_selection.swift index bad7183fafc0f..dbdecc51a83df 100644 --- a/test/SILOptimizer/access_enforcement_selection.swift +++ b/test/SILOptimizer/access_enforcement_selection.swift @@ -98,3 +98,28 @@ public func recaptureStack() -> Int { // The second [read] access is static. Same as `captureStack` above. // // CHECK: Static Access: %{{.*}} = begin_access [read] [static] %{{.*}} : $*Int + + +// ----------------------------------------------------------------------------- +// Inout access in a recursive closure currently has dynamic enforcement +// because enforcement selection cannot visit all closure scopes before +// selecting enforcement within the closure. + +public protocol Apply { + func apply(_ body: (Apply) -> ()) +} + +// CHECK-LABEL: sil private @$s28access_enforcement_selection20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF : $@convention(thin) (@in_guaranteed Apply, @inout_aliasable Int) -> () { +// CHECK: bb0(%0 : $*Apply, %1 : $*Int): +// CHECK: begin_access [modify] [dynamic] %1 : $*Int +// CHECK-LABEL: } // end sil function '$s28access_enforcement_selection20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' +public func testRecursiveClosure(a: Apply, x: inout Int) { + func localFunc(b: Apply) { + x += 1 + let closure = { (c: Apply) in + c.apply(localFunc) + } + b.apply(closure) + } + a.apply(localFunc) +} diff --git a/test/SILOptimizer/closure_scope_analysis.swift b/test/SILOptimizer/closure_scope_analysis.swift new file mode 100644 index 0000000000000..264c172abf916 --- /dev/null +++ b/test/SILOptimizer/closure_scope_analysis.swift @@ -0,0 +1,77 @@ +// RUN: %target-swift-frontend -primary-file %s -emit-sil -O -Xllvm -debug-only=closure-scope -module-name main -o /dev/null 2>&1 | %FileCheck %s + +public protocol Apply { + func apply(_ body: (Apply) -> ()) +} + +public func testRecursiveClosure(a: Apply, x: inout Int) { + func localFunc(b: Apply) { + x += 1 + let closure = { (c: Apply) in + c.apply(localFunc) + } + b.apply(closure) + } + a.apply(localFunc) +} + +public func testDeadClosureCycle(a: Apply, x: inout Int) { + func localFunc(b: Apply) { + x += 1 + let closure = { (c: Apply) in + c.apply(localFunc) + } + b.apply(closure) + } +} + +// The order of the top-level SCOPE nodes is sensitive to the module's +// function list ordering. It may change. The important thing is that: +// +// - the closure scope graph has the same shape +// - the localFunc is marked CYCLE_HEAD +// - the RPO function order does not change + +// testRecursiveClosure scopes: +// +// CHECK: SCOPE: testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF' +// CHECK: CLOSURE: localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' +// CHECK: SCOPE: localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' +// CHECK: CLOSURE: closure #1 in localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' +// CHECK: SCOPE: closure #1 in localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' +// CHECK: CLOSURE: localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' + +// testDeadClosureCycle scopes: +// +// CHECK: SCOPE: localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' +// CHECK: CLOSURE: closure #1 in localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' +// CHECK: SCOPE: closure #1 in localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' +// CHECK: CLOSURE: localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' + +// testRecursiveClosure closures: +// +// CHECK: CLOSURE: localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' +// CHECK: SCOPE: testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF' +// CHECK: SCOPE: closure #1 in localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' +// CHECK: CLOSURE: closure #1 in localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' +// CHECK: SCOPE: localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' + +// testDeadClosureCycle closures: +// +// CHECK: CLOSURE: localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' +// CHECK: SCOPE: closure #1 in localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' +// CHECK: CLOSURE: closure #1 in localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' +// CHECK: SCOPE: localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' + +// CHECK: RPO function order: + +// CHECK: main 'main' +// CHECK: testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF' +// CHECK: CYCLE HEAD: localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' +// CHECK: closure #1 in localFunc #1 (b:) in testRecursiveClosure(a:x:) '$s4main20testRecursiveClosure1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' +// CHECK: Int.init(_builtinIntegerLiteral:) '$sSi22_builtinIntegerLiteralSiBI_tcfC' +// CHECK: static Int.+= infix(_:_:) '$sSi2peoiyySiz_SitFZ' + +// CHECK: testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF' +// CHECK: CYCLE HEAD: localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tF' +// CHECK: closure #1 in localFunc #1 (b:) in testDeadClosureCycle(a:x:) '$s4main20testDeadClosureCycle1a1xyAA5Apply_p_SiztF9localFuncL_1byAaE_p_tFyAaE_pcfU_' From 82fc0590181fd806c972c11b728a53e30e47e15f Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 18 Mar 2022 08:30:03 +0100 Subject: [PATCH 210/242] [CodeCompletion] Store ignored arguments as `Expr *` instead of `ConstraintLocator`s This avoids the construction of `ConstraintLocator`s. --- include/swift/Sema/ConstraintSystem.h | 29 +++++++++++++++++---------- lib/Sema/CSBindings.cpp | 10 ++++++--- lib/Sema/CSGen.cpp | 9 +++------ lib/Sema/CSSimplify.cpp | 11 +++++++--- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 581595f12e7e4..45accdb8d3126 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -2415,7 +2415,7 @@ class ConstraintSystem { /// Arguments after the code completion token that were thus ignored (i.e. /// assigned fresh type variables) for type checking. - llvm::SetVector IgnoredArguments; + llvm::SetVector IgnoredArguments; /// Maps node types used within all portions of the constraint /// system, instead of directly using the types on the @@ -3187,18 +3187,25 @@ class ConstraintSystem { bool containsCodeCompletionLoc(ASTNode node) const; bool containsCodeCompletionLoc(const ArgumentList *args) const; - /// Marks the argument with the \p ArgLoc locator as being ignored because it - /// occurs after the code completion token. This assumes that the argument is - /// not type checked (by assigning it a fresh type variable) and prevents - /// fixes from being generated for this argument. - void markArgumentIgnoredForCodeCompletion(ConstraintLocator *ArgLoc) { - IgnoredArguments.insert(ArgLoc); + /// Marks the argument \p Arg as being ignored because it occurs after the + /// code completion token. This assumes that the argument is not type checked + /// (by assigning it a fresh type variable) and prevents fixes from being + /// generated for this argument. + void markArgumentIgnoredForCodeCompletion(Expr *Arg) { + IgnoredArguments.insert(Arg); } - /// Whether the argument with the \p ArgLoc locator occurs after the code - /// completion tokena and thus should be ignored and not generate any fixes. - bool isArgumentIgnoredForCodeCompletion(ConstraintLocator *ArgLoc) { - return IgnoredArguments.count(ArgLoc) > 0; + /// Whether the argument \p Arg occurs after the code completion token and + /// thus should be ignored and not generate any fixes. + bool isArgumentIgnoredForCodeCompletion(Expr *Arg) const { + return IgnoredArguments.count(Arg) > 0; + } + + /// Whether the constraint system has ignored any arguments for code + /// completion, i.e. whether there is an expression for which + /// \c isArgumentIgnoredForCodeCompletion returns \c true. + bool hasArgumentsIgnoredForCodeCompletion() const { + return !IgnoredArguments.empty(); } void setClosureType(const ClosureExpr *closure, FunctionType *type) { diff --git a/lib/Sema/CSBindings.cpp b/lib/Sema/CSBindings.cpp index 35bc46b4dc381..fa8369c4adb86 100644 --- a/lib/Sema/CSBindings.cpp +++ b/lib/Sema/CSBindings.cpp @@ -2024,9 +2024,13 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const { return false; // Don't penailze solutions that have holes for ignored arguments. - if (cs.isArgumentIgnoredForCodeCompletion( - TypeVar->getImpl().getLocator())) { - return false; + if (cs.hasArgumentsIgnoredForCodeCompletion()) { + // Avoid simplifying the locator if the constriant system didn't ignore + // any arguments. + auto argExpr = simplifyLocatorToAnchor(TypeVar->getImpl().getLocator()); + if (cs.isArgumentIgnoredForCodeCompletion(argExpr.dyn_cast())) { + return false; + } } } // Reflect in the score that this type variable couldn't be diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index fb95be286feb3..6aeb735edda1e 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -788,8 +788,7 @@ namespace { CS(cs) {} std::pair walkToExprPre(Expr *expr) override { - if (CS.isArgumentIgnoredForCodeCompletion( - CS.getConstraintLocator(expr))) { + if (CS.isArgumentIgnoredForCodeCompletion(expr)) { return {false, expr}; } @@ -3648,8 +3647,7 @@ namespace { std::pair walkToExprPre(Expr *expr) override { auto &CS = CG.getConstraintSystem(); - if (CS.isArgumentIgnoredForCodeCompletion( - CS.getConstraintLocator(expr))) { + if (CS.isArgumentIgnoredForCodeCompletion(expr)) { CG.setTypeForArgumentIgnoredForCompletion(expr); return {false, expr}; } @@ -3724,8 +3722,7 @@ namespace { SmallVector ignoredArgs; getArgumentsAfterCodeCompletionToken(expr, CS, ignoredArgs); for (auto ignoredArg : ignoredArgs) { - CS.markArgumentIgnoredForCodeCompletion( - CS.getConstraintLocator(ignoredArg)); + CS.markArgumentIgnoredForCodeCompletion(ignoredArg); } } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index c7b1018c6c4df..66474922bfe56 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -12470,9 +12470,14 @@ bool ConstraintSystem::recordFix(ConstraintFix *fix, unsigned impact) { log << ")\n"; } - if (isArgumentIgnoredForCodeCompletion(fix->getLocator())) { - // The argument was ignored. Don't record any fixes for it. - return false; + if (hasArgumentsIgnoredForCodeCompletion()) { + // Avoid simplifying the locator if the constraint system didn't ignore any + // arguments. + auto argExpr = simplifyLocatorToAnchor(fix->getLocator()); + if (isArgumentIgnoredForCodeCompletion(getAsExpr(argExpr))) { + // The argument was ignored. Don't record any fixes for it. + return false; + } } // Record the fix. From 333d2c2186c09290f9a52f347bbdd5a247d4bcd5 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 18 Mar 2022 08:53:05 -0700 Subject: [PATCH 211/242] Remove invalid install_swiftsyntax_parser preset (#41873) It seems that build-libparser-only hasn't existed since 2019 and this change https://github.com/apple/swift/pull/27871 --- utils/build-presets.ini | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/utils/build-presets.ini b/utils/build-presets.ini index 08551a0ded450..a602806352ba2 100644 --- a/utils/build-presets.ini +++ b/utils/build-presets.ini @@ -2356,17 +2356,6 @@ swift-include-tests=0 darwin-deployment-version-ios=10.0 darwin-crash-reporter-client=1 -[preset: install_swiftsyntax_parser] -release -lto -no-assertions -build-libparser-only -verbose-build -darwin-install-extract-symbols -install-llvm -install-swift - - #===------------------------------------------------------------------------===# # Linux corelibs foundation preset #===------------------------------------------------------------------------===# From 0b32adc9d92b9eb722cdc2ee521c41c423175def Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Fri, 18 Mar 2022 12:44:54 -0400 Subject: [PATCH 212/242] [swift-inspect] Fix taskToThread dictionary on watchOS. watchOS's 32-bit address types continue to cause trouble. rdar://90489466 --- .../Sources/swift-inspect/Operations/DumpConcurrency.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift b/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift index af1aeb0897cf3..89420ffa688c5 100644 --- a/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift +++ b/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift @@ -323,7 +323,7 @@ fileprivate class ConcurrencyDumper { print("warning: unable to decode is-running state of target tasks, running state and async backtraces will not be printed") } - let taskToThread: [swift_addr_t: swift_reflection_ptr_t] = + let taskToThread: [swift_addr_t: UInt64] = Dictionary(threadCurrentTasks.map{ ($1, $0) }, uniquingKeysWith: { $1 }) var lastChilds: [Bool] = [] @@ -364,7 +364,7 @@ fileprivate class ConcurrencyDumper { let flags = decodeTaskFlags(task) output("Task \(hex: task.id) - flags=\(flags) enqueuePriority=\(hex: task.enqueuePriority) maxPriority=\(hex: task.maxPriority) address=\(hex: task.address)") - if let thread = taskToThread[task.address] { + if let thread = taskToThread[swift_addr_t(task.address)] { output("current task on thread \(hex: thread)") } if let parent = task.parent { From 4c9582c29507987591742e5019f46e43f9319add Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Fri, 18 Mar 2022 10:03:07 -0700 Subject: [PATCH 213/242] Revert "Merge pull request #41831 from hyp/unify-header" This reverts commit cd93d23bac1afec44cec0468215073b44b211d61, reversing changes made to f9f5476e9a596e35bc7ccf01bbda44aa4961da81. --- include/swift/AST/DiagnosticsFrontend.def | 4 +- include/swift/Basic/FileTypes.def | 3 +- .../swift/Basic/SupplementaryOutputPaths.h | 29 ++++-- include/swift/Frontend/Frontend.h | 3 +- .../swift/Frontend/FrontendInputsAndOutputs.h | 3 +- include/swift/Option/Options.td | 10 +- include/swift/PrintAsClang/PrintAsClang.h | 16 ++-- lib/Basic/FileTypes.cpp | 9 +- lib/Driver/Driver.cpp | 14 +-- lib/Driver/ToolChains.cpp | 12 ++- .../ArgsToFrontendOptionsConverter.cpp | 7 +- .../ArgsToFrontendOutputsConverter.cpp | 50 ++++++---- lib/Frontend/Frontend.cpp | 9 +- lib/Frontend/FrontendInputsAndOutputs.cpp | 12 ++- lib/Frontend/FrontendOptions.cpp | 3 +- lib/FrontendTool/FrontendTool.cpp | 40 ++++++-- lib/PrintAsClang/PrintAsClang.cpp | 91 +++++++++---------- test/Driver/bridging-pch.swift | 6 +- test/Driver/parseable_output.swift | 2 +- test/Driver/parseable_output_unicode.swift | 2 +- .../supplementary-output-support.swift | 13 ++- .../SwiftToCxx/functions/cdecl-execution.cpp | 2 +- test/Interop/SwiftToCxx/functions/cdecl.swift | 2 +- .../functions/function-availability.swift | 2 +- .../functions/swift-functions-execution.cpp | 2 +- .../functions/swift-functions.swift | 2 +- .../module/module-to-namespace.swift | 2 +- test/PrintAsCxx/empty.swift | 24 ++--- .../Inputs/comments-expected-output.h | 3 - 29 files changed, 220 insertions(+), 157 deletions(-) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index e834a11bd02a1..d9d48be606cb4 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -118,7 +118,9 @@ ERROR(error_mode_cannot_emit_dependencies,none, ERROR(error_mode_cannot_emit_reference_dependencies,none, "this mode does not support emitting reference dependency files", ()) ERROR(error_mode_cannot_emit_header,none, - "this mode does not support emitting Objective-C or C++ headers", ()) + "this mode does not support emitting Objective-C headers", ()) +ERROR(error_mode_cannot_emit_cxx_header,none, + "this mode does not support emitting C++ headers", ()) ERROR(error_mode_cannot_emit_loaded_module_trace,none, "this mode does not support emitting the loaded module trace", ()) ERROR(error_mode_cannot_emit_module,none, diff --git a/include/swift/Basic/FileTypes.def b/include/swift/Basic/FileTypes.def index 2d3c7e3754332..b4490c07c30be 100644 --- a/include/swift/Basic/FileTypes.def +++ b/include/swift/Basic/FileTypes.def @@ -59,7 +59,8 @@ TYPE("raw-sib", RawSIB, "sib", "") TYPE("llvm-ir", LLVM_IR, "ll", "") TYPE("llvm-bc", LLVM_BC, "bc", "") TYPE("diagnostics", SerializedDiagnostics, "dia", "") -TYPE("clang-header", ClangHeader, "h", "") +TYPE("objc-header", ObjCHeader, "h", "") +TYPE("cxx-header", CXXHeader, "h", "") TYPE("swift-dependencies", SwiftDeps, "swiftdeps", "") TYPE("external-swift-dependencies", ExternalSwiftDeps, "swiftdeps.external", "") TYPE("remap", Remapping, "remap", "") diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index c8ba0b075ba0e..68eb39d3d8fba 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -20,8 +20,7 @@ namespace swift { struct SupplementaryOutputPaths { - /// The path to which we should emit a header file that exposes the Swift - /// declarations to C, Objective-C and C++ clients for the module. + /// The path to which we should emit an Objective-C header for the module. /// /// Currently only makes sense when the compiler has whole module knowledge. /// The modes for which it makes sense incuide both WMO and the "merge @@ -29,8 +28,19 @@ struct SupplementaryOutputPaths { /// the header is emitted in single-file mode, since it needs whole-module /// information. /// - /// \sa swift::printAsClangHeader - std::string ClangHeaderOutputPath; + /// \sa swift::printAsObjC + std::string ObjCHeaderOutputPath; + + /// The path to which we should emit a C++ header for the module. + /// + /// Currently only makes sense when the compiler has whole module knowledge. + /// The modes for which it makes sense include both WMO and the "merge + /// modules" job that happens after the normal compilation jobs. That's where + /// the header is emitted in single-file mode, since it needs whole-module + /// information. + /// + /// \sa swift::printAsCXX + std::string CxxHeaderOutputPath; /// The path to which we should emit a serialized module. /// It is valid whenever there are any inputs. @@ -160,8 +170,10 @@ struct SupplementaryOutputPaths { /// Apply a given function for each existing (non-empty string) supplementary output void forEachSetOutput(llvm::function_ref fn) const { - if (!ClangHeaderOutputPath.empty()) - fn(ClangHeaderOutputPath); + if (!ObjCHeaderOutputPath.empty()) + fn(ObjCHeaderOutputPath); + if (!CxxHeaderOutputPath.empty()) + fn(CxxHeaderOutputPath); if (!ModuleOutputPath.empty()) fn(ModuleOutputPath); if (!ModuleSourceInfoOutputPath.empty()) @@ -197,8 +209,9 @@ struct SupplementaryOutputPaths { } bool empty() const { - return ClangHeaderOutputPath.empty() && ModuleOutputPath.empty() && - ModuleDocOutputPath.empty() && DependenciesFilePath.empty() && + return ObjCHeaderOutputPath.empty() && CxxHeaderOutputPath.empty() && + ModuleOutputPath.empty() && ModuleDocOutputPath.empty() && + DependenciesFilePath.empty() && ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 2a35e7c822a15..93bfe7376f7b5 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -392,7 +392,8 @@ class CompilerInvocation { std::string getOutputFilenameForAtMostOnePrimary() const; std::string getMainInputFilenameForDebugInfoForAtMostOnePrimary() const; - std::string getClangHeaderOutputPathForAtMostOnePrimary() const; + std::string getObjCHeaderOutputPathForAtMostOnePrimary() const; + std::string getCxxHeaderOutputPathForAtMostOnePrimary() const; std::string getModuleOutputPathForAtMostOnePrimary() const; std::string getReferenceDependenciesFilePathForPrimary(StringRef filename) const; diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index c28810fb5c84f..572f3a1df17b4 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -249,7 +249,8 @@ class FrontendInputsAndOutputs { bool hasDependenciesPath() const; bool hasReferenceDependenciesPath() const; - bool hasClangHeaderOutputPath() const; + bool hasObjCHeaderOutputPath() const; + bool hasCxxHeaderOutputPath() const; bool hasLoadedModuleTracePath() const; bool hasModuleOutputPath() const; bool hasModuleDocOutputPath() const; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index faae3e2a0db28..e8787c5074617 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -532,11 +532,13 @@ def emit_objc_header_path : Separate<["-"], "emit-objc-header-path">, SupplementaryOutput]>, MetaVarName<"">, HelpText<"Emit an Objective-C header file to ">; -def emit_clang_header_path : Separate<["-"], "emit-clang-header-path">, - Flags<[FrontendOption, NoDriverOption, NoInteractiveOption, ArgumentIsPath, +def emit_cxx_header : Flag<["-"], "emit-cxx-header">, + Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>, + HelpText<"Emit a C++ header file">; +def emit_cxx_header_path : Separate<["-"], "emit-cxx-header-path">, + Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath, SupplementaryOutput]>, - HelpText<"Emit an Objective-C and C++ header file to ">, - Alias; + MetaVarName<"">, HelpText<"Emit a C++ header file to ">; def static : Flag<["-"], "static">, Flags<[FrontendOption, ModuleInterfaceOption, NoInteractiveOption]>, diff --git a/include/swift/PrintAsClang/PrintAsClang.h b/include/swift/PrintAsClang/PrintAsClang.h index a52699b330dc4..867f75fd245e8 100644 --- a/include/swift/PrintAsClang/PrintAsClang.h +++ b/include/swift/PrintAsClang/PrintAsClang.h @@ -21,18 +21,16 @@ namespace swift { class ModuleDecl; class ValueDecl; - /// Print the exposed declarations in a module into a Clang header. + /// Print the Objective-C-compatible declarations in a module as a Clang + /// header. /// - /// The Objective-C compatible declarations are printed into a block that - /// ensures that those declarations are only usable when the header is - /// compiled in Objective-C mode. - /// The C++ compatible declarations are printed into a block that ensures - /// that those declarations are only usable when the header is compiled in - /// C++ mode. + /// Returns true on error. + bool printAsObjC(raw_ostream &out, ModuleDecl *M, StringRef bridgingHeader); + + /// Print the C++-compatible declarations in a module as a Clang header. /// /// Returns true on error. - bool printAsClangHeader(raw_ostream &out, ModuleDecl *M, - StringRef bridgingHeader); + bool printAsCXX(raw_ostream &os, ModuleDecl *M); } #endif diff --git a/lib/Basic/FileTypes.cpp b/lib/Basic/FileTypes.cpp index 607c4cece5879..e8d6f5c50ec7c 100644 --- a/lib/Basic/FileTypes.cpp +++ b/lib/Basic/FileTypes.cpp @@ -73,7 +73,8 @@ bool file_types::isTextual(ID Id) { case file_types::TY_ASTDump: case file_types::TY_RawSIL: case file_types::TY_LLVM_IR: - case file_types::TY_ClangHeader: + case file_types::TY_ObjCHeader: + case file_types::TY_CXXHeader: case file_types::TY_AutolinkFile: case file_types::TY_ImportedModules: case file_types::TY_TBD: @@ -131,7 +132,8 @@ bool file_types::isAfterLLVM(ID Id) { case file_types::TY_Dependencies: case file_types::TY_ASTDump: case file_types::TY_RawSIL: - case file_types::TY_ClangHeader: + case file_types::TY_ObjCHeader: + case file_types::TY_CXXHeader: case file_types::TY_AutolinkFile: case file_types::TY_Image: case file_types::TY_dSYM: @@ -181,7 +183,8 @@ bool file_types::isPartOfSwiftCompilation(ID Id) { case file_types::TY_LLVM_BC: case file_types::TY_Object: case file_types::TY_Dependencies: - case file_types::TY_ClangHeader: + case file_types::TY_ObjCHeader: + case file_types::TY_CXXHeader: case file_types::TY_AutolinkFile: case file_types::TY_PCH: case file_types::TY_ImportedModules: diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index a7359bb9c9ac9..02d9cf654016c 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1981,7 +1981,8 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, if (Arg *A = Args.getLastArg(options::OPT_import_objc_header)) { StringRef Value = A->getValue(); auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value)); - if (Ty == file_types::TY_ClangHeader) { + if (Ty == file_types::TY_ObjCHeader || + Ty == file_types::TY_CXXHeader) { auto *HeaderInput = C.createAction(*A, Ty); StringRef PersistentPCHDir; if (const Arg *A = Args.getLastArg(options::OPT_pch_output_dir)) { @@ -2064,7 +2065,8 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, case file_types::TY_LLVM_IR: case file_types::TY_LLVM_BC: case file_types::TY_SerializedDiagnostics: - case file_types::TY_ClangHeader: + case file_types::TY_ObjCHeader: + case file_types::TY_CXXHeader: case file_types::TY_ClangModuleFile: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -3478,12 +3480,12 @@ void Driver::chooseObjectiveCHeaderOutputPath(Compilation &C, StringRef workingDirectory, CommandOutput *Output) const { - if (hasExistingAdditionalOutput(*Output, file_types::TY_ClangHeader)) + if (hasExistingAdditionalOutput(*Output, file_types::TY_ObjCHeader)) return; StringRef ObjCHeaderPath; if (OutputMap) { - auto iter = OutputMap->find(file_types::TY_ClangHeader); + auto iter = OutputMap->find(file_types::TY_ObjCHeader); if (iter != OutputMap->end()) ObjCHeaderPath = iter->second; } @@ -3493,13 +3495,13 @@ void Driver::chooseObjectiveCHeaderOutputPath(Compilation &C, ObjCHeaderPath = A->getValue(); if (!ObjCHeaderPath.empty()) { - Output->setAdditionalOutputForType(file_types::TY_ClangHeader, + Output->setAdditionalOutputForType(file_types::TY_ObjCHeader, ObjCHeaderPath); } else { // Put the header next to the primary output file. // FIXME: That's not correct if the user /just/ passed -emit-header // and not -emit-module. - addAuxiliaryOutput(C, *Output, file_types::TY_ClangHeader, + addAuxiliaryOutput(C, *Output, file_types::TY_ObjCHeader, /*output file map*/ nullptr, workingDirectory); } } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 62b6ddc211d5f..aaa7a6b7299c8 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -675,7 +675,8 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const { case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: case file_types::TY_SerializedDiagnostics: - case file_types::TY_ClangHeader: + case file_types::TY_ObjCHeader: + case file_types::TY_CXXHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -814,7 +815,7 @@ void ToolChain::JobContext::addFrontendSupplementaryOutputArguments( file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); - if (addOutputsOfType(arguments, Output, Args, file_types::ID::TY_ClangHeader, + if (addOutputsOfType(arguments, Output, Args, file_types::ID::TY_ObjCHeader, "-emit-objc-header-path")) { assert(OI.CompilerMode == OutputInfo::Mode::SingleCompile && "The Swift tool should only emit an Obj-C header in single compile" @@ -935,7 +936,8 @@ ToolChain::constructInvocation(const BackendJobAction &job, case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: case file_types::TY_SerializedDiagnostics: - case file_types::TY_ClangHeader: + case file_types::TY_ObjCHeader: + case file_types::TY_CXXHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -1098,7 +1100,7 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); addOutputsOfType(Arguments, context.Output, context.Args, - file_types::TY_ClangHeader, "-emit-objc-header-path"); + file_types::TY_ObjCHeader, "-emit-objc-header-path"); addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_TBD, "-emit-tbd-path"); @@ -1307,7 +1309,7 @@ ToolChain::constructInvocation(const GeneratePCHJobAction &job, file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); - addInputsOfType(Arguments, context.InputActions, file_types::TY_ClangHeader); + addInputsOfType(Arguments, context.InputActions, file_types::TY_ObjCHeader); context.Args.AddLastArg(Arguments, options::OPT_index_store_path); if (job.isPersistentPCH()) { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 02071f42c0c8d..0526af38d1a3f 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -625,10 +625,15 @@ bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths() return true; } if (!FrontendOptions::canActionEmitClangHeader(Opts.RequestedAction) && - Opts.InputsAndOutputs.hasClangHeaderOutputPath()) { + Opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_header); return true; } + if (!FrontendOptions::canActionEmitClangHeader(Opts.RequestedAction) && + Opts.InputsAndOutputs.hasCxxHeaderOutputPath()) { + Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_cxx_header); + return true; + } if (!FrontendOptions::canActionEmitLoadedModuleTrace(Opts.RequestedAction) && Opts.InputsAndOutputs.hasLoadedModuleTracePath()) { Diags.diagnose(SourceLoc(), diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index 1ebaa9837f3cd..7095d391129d6 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -308,8 +308,10 @@ Optional> SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() const { - auto clangHeaderOutput = getSupplementaryFilenamesFromArguments( + auto objCHeaderOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_objc_header_path); + auto cxxHeaderOutput = + getSupplementaryFilenamesFromArguments(options::OPT_emit_cxx_header_path); auto moduleOutput = getSupplementaryFilenamesFromArguments(options::OPT_emit_module_path); auto moduleDocOutput = @@ -339,8 +341,8 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_module_semantic_info_path); auto optRecordOutput = getSupplementaryFilenamesFromArguments( options::OPT_save_optimization_record_path); - if (!clangHeaderOutput || !moduleOutput || !moduleDocOutput || - !dependenciesFile || !referenceDependenciesFile || + if (!objCHeaderOutput || !cxxHeaderOutput || !moduleOutput || + !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || !moduleInterfaceOutput || !privateModuleInterfaceOutput || !moduleSourceInfoOutput || !moduleSummaryOutput || !abiDescriptorOutput || @@ -353,7 +355,8 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() InputsAndOutputs.countOfFilesProducingSupplementaryOutput(); for (unsigned i = 0; i < N; ++i) { SupplementaryOutputPaths sop; - sop.ClangHeaderOutputPath = (*clangHeaderOutput)[i]; + sop.ObjCHeaderOutputPath = (*objCHeaderOutput)[i]; + sop.CxxHeaderOutputPath = (*cxxHeaderOutput)[i]; sop.ModuleOutputPath = (*moduleOutput)[i]; sop.ModuleDocOutputPath = (*moduleDocOutput)[i]; sop.DependenciesFilePath = (*dependenciesFile)[i]; @@ -434,9 +437,14 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( // There is no non-path form of -emit-fixits-path auto fixItsOutputPath = pathsFromArguments.FixItsOutputPath; - auto clangHeaderOutputPath = determineSupplementaryOutputFilename( - OPT_emit_objc_header, pathsFromArguments.ClangHeaderOutputPath, - file_types::TY_ClangHeader, "", + auto objcHeaderOutputPath = determineSupplementaryOutputFilename( + OPT_emit_objc_header, pathsFromArguments.ObjCHeaderOutputPath, + file_types::TY_ObjCHeader, "", + defaultSupplementaryOutputPathExcludingExtension); + + auto cxxHeaderOutputPath = determineSupplementaryOutputFilename( + OPT_emit_cxx_header, pathsFromArguments.CxxHeaderOutputPath, + file_types::TY_CXXHeader, "", defaultSupplementaryOutputPathExcludingExtension); auto loadedModuleTracePath = determineSupplementaryOutputFilename( @@ -492,7 +500,8 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( defaultSupplementaryOutputPathExcludingExtension); SupplementaryOutputPaths sop; - sop.ClangHeaderOutputPath = clangHeaderOutputPath; + sop.ObjCHeaderOutputPath = objcHeaderOutputPath; + sop.CxxHeaderOutputPath = cxxHeaderOutputPath; sop.ModuleOutputPath = moduleOutputPath; sop.ModuleDocOutputPath = moduleDocOutputPath; sop.DependenciesFilePath = dependenciesFilePath; @@ -577,7 +586,8 @@ createFromTypeToPathMap(const TypeToPathMap *map) { if (!map) return paths; const std::pair typesAndStrings[] = { - {file_types::TY_ClangHeader, paths.ClangHeaderOutputPath}, + {file_types::TY_ObjCHeader, paths.ObjCHeaderOutputPath}, + {file_types::TY_CXXHeader, paths.CxxHeaderOutputPath}, {file_types::TY_SwiftModuleFile, paths.ModuleOutputPath}, {file_types::TY_SwiftModuleDocFile, paths.ModuleDocOutputPath}, {file_types::TY_SwiftSourceInfoFile, paths.ModuleSourceInfoOutputPath}, @@ -605,17 +615,17 @@ createFromTypeToPathMap(const TypeToPathMap *map) { Optional> SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const { - if (Arg *A = Args.getLastArg(options::OPT_emit_objc_header_path, - options::OPT_emit_module_path, - options::OPT_emit_module_doc_path, - options::OPT_emit_dependencies_path, - options::OPT_emit_reference_dependencies_path, - options::OPT_serialize_diagnostics_path, - options::OPT_emit_loaded_module_trace_path, - options::OPT_emit_module_interface_path, - options::OPT_emit_private_module_interface_path, - options::OPT_emit_module_source_info_path, - options::OPT_emit_tbd_path)) { + if (Arg *A = Args.getLastArg( + options::OPT_emit_objc_header_path, options::OPT_emit_cxx_header_path, + options::OPT_emit_module_path, options::OPT_emit_module_doc_path, + options::OPT_emit_dependencies_path, + options::OPT_emit_reference_dependencies_path, + options::OPT_serialize_diagnostics_path, + options::OPT_emit_loaded_module_trace_path, + options::OPT_emit_module_interface_path, + options::OPT_emit_private_module_interface_path, + options::OPT_emit_module_source_info_path, + options::OPT_emit_tbd_path)) { Diags.diagnose(SourceLoc(), diag::error_cannot_have_supplementary_outputs, A->getSpelling(), "-supplementary-output-file-map"); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 2fe54499d1fdc..f8a5ee1b462ed 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -92,9 +92,14 @@ CompilerInvocation::getMainInputFilenameForDebugInfoForAtMostOnePrimary() .MainInputFilenameForDebugInfo; } std::string -CompilerInvocation::getClangHeaderOutputPathForAtMostOnePrimary() const { +CompilerInvocation::getObjCHeaderOutputPathForAtMostOnePrimary() const { return getPrimarySpecificPathsForAtMostOnePrimary() - .SupplementaryOutputs.ClangHeaderOutputPath; + .SupplementaryOutputs.ObjCHeaderOutputPath; +} +std::string +CompilerInvocation::getCxxHeaderOutputPathForAtMostOnePrimary() const { + return getPrimarySpecificPathsForAtMostOnePrimary() + .SupplementaryOutputs.CxxHeaderOutputPath; } std::string CompilerInvocation::getModuleOutputPathForAtMostOnePrimary() const { return getPrimarySpecificPathsForAtMostOnePrimary() diff --git a/lib/Frontend/FrontendInputsAndOutputs.cpp b/lib/Frontend/FrontendInputsAndOutputs.cpp index cb6b4ad2dc515..59d12af5c142a 100644 --- a/lib/Frontend/FrontendInputsAndOutputs.cpp +++ b/lib/Frontend/FrontendInputsAndOutputs.cpp @@ -213,7 +213,7 @@ bool FrontendInputsAndOutputs::shouldTreatAsObjCHeader() const { if (hasSingleInput()) { StringRef InputExt = llvm::sys::path::extension(getFilenameOfFirstInput()); switch (file_types::lookupTypeForExtension(InputExt)) { - case file_types::TY_ClangHeader: + case file_types::TY_ObjCHeader: return true; default: return false; @@ -461,10 +461,16 @@ bool FrontendInputsAndOutputs::hasReferenceDependenciesPath() const { return outs.ReferenceDependenciesFilePath; }); } -bool FrontendInputsAndOutputs::hasClangHeaderOutputPath() const { +bool FrontendInputsAndOutputs::hasObjCHeaderOutputPath() const { return hasSupplementaryOutputPath( [](const SupplementaryOutputPaths &outs) -> const std::string & { - return outs.ClangHeaderOutputPath; + return outs.ObjCHeaderOutputPath; + }); +} +bool FrontendInputsAndOutputs::hasCxxHeaderOutputPath() const { + return hasSupplementaryOutputPath( + [](const SupplementaryOutputPaths &outs) -> const std::string & { + return outs.CxxHeaderOutputPath; }); } bool FrontendInputsAndOutputs::hasLoadedModuleTracePath() const { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index 84ecf13f446bb..d956b71d203c1 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -234,7 +234,8 @@ void FrontendOptions::forAllOutputPaths( const std::string *outputs[] = { &outs.ModuleOutputPath, &outs.ModuleDocOutputPath, &outs.ModuleInterfaceOutputPath, &outs.PrivateModuleInterfaceOutputPath, - &outs.ClangHeaderOutputPath, &outs.ModuleSourceInfoOutputPath}; + &outs.ObjCHeaderOutputPath, &outs.CxxHeaderOutputPath, + &outs.ModuleSourceInfoOutputPath}; for (const std::string *next : outputs) { if (!next->empty()) fn(*next); diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 9317a20864f24..095c3662e4084 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -170,22 +170,36 @@ static bool writeSIL(SILModule &SM, const PrimarySpecificPaths &PSPs, /// Prints the Objective-C "generated header" interface for \p M to \p /// outputPath. -/// Print the exposed "generated header" interface for \p M to \p -/// outputPath. /// /// ...unless \p outputPath is empty, in which case it does nothing. /// /// \returns true if there were any errors /// -/// \see swift::printAsClangHeader -static bool printAsClangHeaderIfNeeded(StringRef outputPath, ModuleDecl *M, - StringRef bridgingHeader) { +/// \see swift::printAsObjC +static bool printAsObjCIfNeeded(StringRef outputPath, ModuleDecl *M, + StringRef bridgingHeader) { if (outputPath.empty()) return false; return withOutputFile(M->getDiags(), outputPath, [&](raw_ostream &out) -> bool { - return printAsClangHeader(out, M, bridgingHeader); - }); + return printAsObjC(out, M, bridgingHeader); + }); +} + +/// Prints the C++ "generated header" interface for \p M to \p +/// outputPath. +/// +/// ...unless \p outputPath is empty, in which case it does nothing. +/// +/// \returns true if there were any errors +/// +/// \see swift::printAsCXX +static bool printAsCxxIfNeeded(StringRef outputPath, ModuleDecl *M) { + if (outputPath.empty()) + return false; + return withOutputFile( + M->getDiags(), outputPath, + [&](raw_ostream &os) -> bool { return printAsCXX(os, M); }); } /// Prints the stable module interface for \p M to \p outputPath. @@ -810,7 +824,7 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( bool hadAnyError = false; if ((!Context.hadError() || opts.AllowModuleWithCompilerErrors) && - opts.InputsAndOutputs.hasClangHeaderOutputPath()) { + opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { std::string BridgingHeaderPathForPrint; if (!opts.ImplicitObjCHeaderPath.empty()) { if (opts.BridgingHeaderDirForPrint.hasValue()) { @@ -824,10 +838,16 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( BridgingHeaderPathForPrint = opts.ImplicitObjCHeaderPath; } } - hadAnyError |= printAsClangHeaderIfNeeded( - Invocation.getClangHeaderOutputPathForAtMostOnePrimary(), + hadAnyError |= printAsObjCIfNeeded( + Invocation.getObjCHeaderOutputPathForAtMostOnePrimary(), Instance.getMainModule(), BridgingHeaderPathForPrint); } + if ((!Context.hadError() || opts.AllowModuleWithCompilerErrors) && + opts.InputsAndOutputs.hasCxxHeaderOutputPath()) { + hadAnyError |= printAsCxxIfNeeded( + Invocation.getCxxHeaderOutputPathForAtMostOnePrimary(), + Instance.getMainModule()); + } // Only want the header if there's been any errors, ie. there's not much // point outputting a swiftinterface for an invalid module diff --git a/lib/PrintAsClang/PrintAsClang.cpp b/lib/PrintAsClang/PrintAsClang.cpp index 116067978d1e1..f6910759a596e 100644 --- a/lib/PrintAsClang/PrintAsClang.cpp +++ b/lib/PrintAsClang/PrintAsClang.cpp @@ -26,32 +26,28 @@ using namespace swift; -static void emitCxxConditional(raw_ostream &out, - llvm::function_ref cxxCase, - llvm::function_ref cCase = {}) { - out << "#if defined(__cplusplus)\n"; - cxxCase(); - if (cCase) { - out << "#else\n"; - cCase(); - } - out << "#endif\n"; -} - -static void emitObjCConditional(raw_ostream &out, - llvm::function_ref objcCase, - llvm::function_ref nonObjCCase = {}) { - out << "#if defined(__OBJC__)\n"; - objcCase(); - if (nonObjCCase) { - out << "#else\n"; - nonObjCCase(); - } - out << "#endif\n"; -} - static void writePrologue(raw_ostream &out, ASTContext &ctx, StringRef macroGuard) { + auto emitCxxConditional = [&](llvm::function_ref cxxCase, + llvm::function_ref cCase = {}) { + out << "#if defined(__cplusplus)\n"; + cxxCase(); + if (cCase) { + out << "#else\n"; + cCase(); + } + out << "#endif\n"; + }; + auto emitObjCConditional = [&](llvm::function_ref objcCase, + llvm::function_ref nonObjCCase = {}) { + out << "#if defined(__OBJC__)\n"; + objcCase(); + if (nonObjCCase) { + out << "#else\n"; + nonObjCCase(); + } + out << "#endif\n"; + }; out << "// Generated by " << version::getSwiftFullVersion(ctx.LangOpts.EffectiveLanguageVersion) @@ -81,10 +77,8 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, "#endif\n" "\n" "#pragma clang diagnostic ignored \"-Wauto-import\"\n"; - emitObjCConditional(out, - [&] { out << "#include \n"; }); + emitObjCConditional([&] { out << "#include \n"; }); emitCxxConditional( - out, [&] { out << "#include \n" "#include \n" @@ -282,7 +276,7 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, "#else\n" "# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)\n" "#endif\n"; - emitObjCConditional(out, [&] { + emitObjCConditional([&] { out << "#if !defined(IBSegueAction)\n" "# define IBSegueAction\n" "#endif\n"; @@ -301,9 +295,8 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, }; emitMacro("SWIFT_CALL", "__attribute__((swiftcall))"); // SWIFT_NOEXCEPT applies 'noexcept' in C++ mode only. - emitCxxConditional( - out, [&] { emitMacro("SWIFT_NOEXCEPT", "noexcept"); }, - [&] { emitMacro("SWIFT_NOEXCEPT"); }); + emitCxxConditional([&] { emitMacro("SWIFT_NOEXCEPT", "noexcept"); }, + [&] { emitMacro("SWIFT_NOEXCEPT"); }); static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, "need to add SIMD typedefs here if max elements is increased"); } @@ -449,33 +442,33 @@ static std::string computeMacroGuard(const ModuleDecl *M) { return (llvm::Twine(M->getNameStr().upper()) + "_SWIFT_H").str(); } -static std::string getModuleContentsCxxString(ModuleDecl &M) { +bool swift::printAsObjC(raw_ostream &os, ModuleDecl *M, + StringRef bridgingHeader) { + llvm::PrettyStackTraceString trace("While generating Objective-C header"); + SmallPtrSet imports; std::string moduleContentsBuf; llvm::raw_string_ostream moduleContents{moduleContentsBuf}; - printModuleContentsAsCxx(moduleContents, imports, M); - return moduleContents.str(); + printModuleContentsAsObjC(moduleContents, imports, *M); + writePrologue(os, M->getASTContext(), computeMacroGuard(M)); + writeImports(os, imports, *M, bridgingHeader); + writePostImportPrologue(os, *M); + os << moduleContents.str(); + writeEpilogue(os); + + return false; } -bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M, - StringRef bridgingHeader) { - llvm::PrettyStackTraceString trace("While generating Clang header"); +bool swift::printAsCXX(raw_ostream &os, ModuleDecl *M) { + llvm::PrettyStackTraceString trace("While generating C++ header"); SmallPtrSet imports; - std::string objcModuleContentsBuf; - llvm::raw_string_ostream objcModuleContents{objcModuleContentsBuf}; - printModuleContentsAsObjC(objcModuleContents, imports, *M); + std::string moduleContentsBuf; + llvm::raw_string_ostream moduleContents{moduleContentsBuf}; + printModuleContentsAsCxx(moduleContents, imports, *M); writePrologue(os, M->getASTContext(), computeMacroGuard(M)); - emitObjCConditional(os, - [&] { writeImports(os, imports, *M, bridgingHeader); }); writePostImportPrologue(os, *M); - emitObjCConditional(os, [&] { os << objcModuleContents.str(); }); - emitCxxConditional(os, [&] { - // FIXME: Expose Swift with @expose by default. - if (M->getASTContext().LangOpts.EnableCXXInterop) { - os << getModuleContentsCxxString(*M); - } - }); + os << moduleContents.str(); writeEpilogue(os); return false; diff --git a/test/Driver/bridging-pch.swift b/test/Driver/bridging-pch.swift index 8750a013d2e64..4e585f74d7592 100644 --- a/test/Driver/bridging-pch.swift +++ b/test/Driver/bridging-pch.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift -typecheck -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=YESPCHACT -// YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header +// YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header // YESPCHACT: 1: generate-pch, {0}, pch // YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift // YESPCHACT: 3: compile, {2, 1}, none @@ -30,13 +30,13 @@ // Test persistent PCH // RUN: %target-build-swift -typecheck -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACT -// PERSISTENT-YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header +// PERSISTENT-YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header // PERSISTENT-YESPCHACT: 1: generate-pch, {0}, none // PERSISTENT-YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift // PERSISTENT-YESPCHACT: 3: compile, {2, 1}, none // RUN: %target-build-swift -c -driver-print-actions -embed-bitcode -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACTBC -// PERSISTENT-YESPCHACTBC: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header +// PERSISTENT-YESPCHACTBC: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header // PERSISTENT-YESPCHACTBC: 1: generate-pch, {0}, none // PERSISTENT-YESPCHACTBC: 2: input, "{{.*}}bridging-pch.swift", swift // PERSISTENT-YESPCHACTBC: 3: compile, {2, 1}, llvm-bc diff --git a/test/Driver/parseable_output.swift b/test/Driver/parseable_output.swift index 46458c013c404..b955298cfd568 100644 --- a/test/Driver/parseable_output.swift +++ b/test/Driver/parseable_output.swift @@ -94,7 +94,7 @@ // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output.swift.tmp.swiftsourceinfo" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "clang-header", +// CHECK-NEXT: "type": "objc-header", // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output.swift.tmp.h" // CHECK-NEXT: } // CHECK-NEXT: ], diff --git a/test/Driver/parseable_output_unicode.swift b/test/Driver/parseable_output_unicode.swift index f8e8925cfe430..e9065ee92be91 100644 --- a/test/Driver/parseable_output_unicode.swift +++ b/test/Driver/parseable_output_unicode.swift @@ -96,7 +96,7 @@ // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output_unicode.swift.tmp.swiftsourceinfo" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "clang-header", +// CHECK-NEXT: "type": "objc-header", // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output_unicode.swift.tmp.h" // CHECK-NEXT: } // CHECK-NEXT: ], diff --git a/test/Frontend/supplementary-output-support.swift b/test/Frontend/supplementary-output-support.swift index 30fa6bf7406ef..fc34e980ef885 100644 --- a/test/Frontend/supplementary-output-support.swift +++ b/test/Frontend/supplementary-output-support.swift @@ -18,11 +18,18 @@ // RESOLVE_IMPORTS_NO_REFERENCE_DEPS: error: this mode does not support emitting reference dependency files{{$}} // RUN: not %target-swift-frontend -parse -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_OBJC_HEADER %s -// PARSE_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C or C++ headers{{$}} +// PARSE_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}} // RUN: not %target-swift-frontend -dump-ast -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=DUMP_NO_OBJC_HEADER %s -// DUMP_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C or C++ headers{{$}} +// DUMP_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}} // RUN: not %target-swift-frontend -resolve-imports -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=RESOLVE_IMPORTS_NO_OBJC_HEADER %s -// RESOLVE_IMPORTS_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C or C++ headers{{$}} +// RESOLVE_IMPORTS_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}} + +// RUN: not %target-swift-frontend -parse -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_CXX_HEADER %s +// PARSE_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}} +// RUN: not %target-swift-frontend -dump-ast -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=DUMP_NO_CXX_HEADER %s +// DUMP_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}} +// RUN: not %target-swift-frontend -resolve-imports -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=RESOLVE_IMPORTS_NO_CXX_HEADER %s +// RESOLVE_IMPORTS_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}} // RUN: not %target-swift-frontend -parse -emit-module-interface-path %t %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_INTERFACE %s // PARSE_NO_INTERFACE: error: this mode does not support emitting module interface files{{$}} diff --git a/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp b/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp index ecef6bad7f3a9..130434cbefdd4 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp +++ b/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %S/cdecl.swift -typecheck -module-name CdeclFunctions -enable-cxx-interop -emit-clang-header-path %t/functions.h +// RUN: %target-swift-frontend %S/cdecl.swift -typecheck -module-name CdeclFunctions -emit-cxx-header-path %t/functions.h // RUN: %target-interop-build-clangxx -c %s -I %t -o %t/cdecl-execution.o // RUN: %target-interop-build-swift %S/cdecl.swift -o %t/cdecl-execution -Xlinker %t/cdecl-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain diff --git a/test/Interop/SwiftToCxx/functions/cdecl.swift b/test/Interop/SwiftToCxx/functions/cdecl.swift index 94055be7bee29..8b1c73585b9d1 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl.swift +++ b/test/Interop/SwiftToCxx/functions/cdecl.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name CdeclFunctions -enable-cxx-interop -emit-clang-header-path %t/cdecl.h +// RUN: %target-swift-frontend %s -typecheck -module-name CdeclFunctions -emit-cxx-header-path %t/cdecl.h // RUN: %FileCheck %s < %t/cdecl.h // RUN: %check-interop-cxx-header-in-clang(%t/cdecl.h) diff --git a/test/Interop/SwiftToCxx/functions/function-availability.swift b/test/Interop/SwiftToCxx/functions/function-availability.swift index b3d9f7ca116fb..54f9dfb1d958e 100644 --- a/test/Interop/SwiftToCxx/functions/function-availability.swift +++ b/test/Interop/SwiftToCxx/functions/function-availability.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h +// RUN: %target-swift-frontend %s -typecheck -module-name Functions -emit-cxx-header-path %t/functions.h // RUN: %FileCheck %s < %t/functions.h // RUN: %check-interop-cxx-header-in-clang(%t/functions.h) diff --git a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp index e9c834d7b87eb..08a7d0c5475a9 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp +++ b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %S/swift-functions.swift -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h +// RUN: %target-swift-frontend %S/swift-functions.swift -typecheck -module-name Functions -emit-cxx-header-path %t/functions.h // RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-functions-execution.o // RUN: %target-interop-build-swift %S/swift-functions.swift -o %t/swift-functions-execution -Xlinker %t/swift-functions-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain diff --git a/test/Interop/SwiftToCxx/functions/swift-functions.swift b/test/Interop/SwiftToCxx/functions/swift-functions.swift index 6b889d20959c0..e37e24bff13ce 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions.swift +++ b/test/Interop/SwiftToCxx/functions/swift-functions.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h +// RUN: %target-swift-frontend %s -typecheck -module-name Functions -emit-cxx-header-path %t/functions.h // RUN: %FileCheck %s < %t/functions.h // RUN: %check-interop-cxx-header-in-clang(%t/functions.h) diff --git a/test/Interop/SwiftToCxx/module/module-to-namespace.swift b/test/Interop/SwiftToCxx/module/module-to-namespace.swift index db53e90cd3167..eeba2992d63b5 100644 --- a/test/Interop/SwiftToCxx/module/module-to-namespace.swift +++ b/test/Interop/SwiftToCxx/module/module-to-namespace.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Test -enable-cxx-interop -emit-clang-header-path %t/empty.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -module-name Test -emit-cxx-header-path %t/empty.h // RUN: %FileCheck %s < %t/empty.h // RUN: %check-interop-cxx-header-in-clang(%t/empty.h) diff --git a/test/PrintAsCxx/empty.swift b/test/PrintAsCxx/empty.swift index 317d3a8416f12..0b3c6c64c9b07 100644 --- a/test/PrintAsCxx/empty.swift +++ b/test/PrintAsCxx/empty.swift @@ -1,7 +1,13 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -enable-cxx-interop -emit-clang-header-path %t/empty.h +// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -emit-cxx-header-path %t/empty.h // RUN: %FileCheck %s < %t/empty.h +// RUN: %check-cxx-header-in-clang -std=c++14 %t/empty.h +// RUN: %check-cxx-header-in-clang -std=c++17 %t/empty.h + +// CHECK-NOT: @import Swift; +// CHECK-NOT: IBSegueAction + // CHECK-LABEL: #ifndef EMPTY_SWIFT_H // CHECK-NEXT: #define EMPTY_SWIFT_H @@ -57,19 +63,7 @@ // CHECK: # define SWIFT_EXTENSION(M) // CHECK: # define OBJC_DESIGNATED_INITIALIZER -// CHECK-LABEL: #if defined(__OBJC__) -// CHECK-NEXT: #if !defined(IBSegueAction) -// CHECK-NEXT: # define IBSegueAction -// CHECK-NEXT: #endif - -// CHECK-LABEL: #if defined(__OBJC__) -// CHECK-NEXT: #if __has_feature(modules) - -// CHECK-LABEL: #if defined(__OBJC__) -// CHECK-NEXT: #endif -// CHECK-NEXT: #if defined(__cplusplus) -// CHECK-NEXT: namespace empty { -// CHECK: } // namespace empty -// CHECK: #endif +// CHECK-LABEL: namespace empty { +// CHECK: } // namespace empty // CHECK-NOT: @ diff --git a/test/PrintAsObjC/Inputs/comments-expected-output.h b/test/PrintAsObjC/Inputs/comments-expected-output.h index 2a23d5ed3d579..eccfc9b978141 100644 --- a/test/PrintAsObjC/Inputs/comments-expected-output.h +++ b/test/PrintAsObjC/Inputs/comments-expected-output.h @@ -424,9 +424,6 @@ SWIFT_CLASS("_TtC8comments13UnorderedList") - (void)f0; @end -#endif -#if defined(__cplusplus) -#endif #if __has_attribute(external_source_symbol) # pragma clang attribute pop #endif From adec767b2db399d4b6fe423247c97514940ffca3 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Fri, 18 Mar 2022 13:05:29 -0400 Subject: [PATCH 214/242] [Concurrency] Have StackAllocator gracefully fail when passed a small firstSlabBuffer. Creating a task for an async let will attempt to allocate the task in the async let's preallocated space, and if there's any space left over then it will use the extra for the new tasks's allocator. However, StackAllocator requires the preallocated buffer to be large enough to hold a slab header, but the task creation code doesn't check for that. As a result, the task creation code can end up passing a buffer that's too small to hold the slab header. When asserts are enabled, this is caught by an assert. When asserts are not enabled, disaster strikes. We end up computing a negative number for the remaining slab capacity, which underflows to a large positive number, causing the slab to overflow the async let's preallocated space. This results in weird memory corruption. SR-15996 rdar://90357994 --- stdlib/public/runtime/StackAllocator.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/public/runtime/StackAllocator.h b/stdlib/public/runtime/StackAllocator.h index 24e1315e7d47f..2f23ff41cf1c5 100644 --- a/stdlib/public/runtime/StackAllocator.h +++ b/stdlib/public/runtime/StackAllocator.h @@ -283,7 +283,10 @@ class StackAllocator { numAllocatedSlabs(0) {} /// Construct a StackAllocator with a pre-allocated first slab. - StackAllocator(void *firstSlabBuffer, size_t bufferCapacity) { + StackAllocator(void *firstSlabBuffer, size_t bufferCapacity) : StackAllocator() { + // If the pre-allocated buffer can't hold a slab header, ignore it. + if (bufferCapacity <= Slab::headerSize()) + return; char *start = (char *)llvm::alignAddr(firstSlabBuffer, llvm::Align(alignment)); char *end = (char *)firstSlabBuffer + bufferCapacity; From 0ba00dbf66a25bf345cd2e11ad99dacdac3a00d4 Mon Sep 17 00:00:00 2001 From: Nate Chandler Date: Fri, 18 Mar 2022 11:21:19 -0700 Subject: [PATCH 215/242] [Test] Disabled a test temporarily. rdar://90495704 --- test/SILOptimizer/performance-annotations.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/SILOptimizer/performance-annotations.swift b/test/SILOptimizer/performance-annotations.swift index 8c99d9ed1bebc..ccbe60db1beb9 100644 --- a/test/SILOptimizer/performance-annotations.swift +++ b/test/SILOptimizer/performance-annotations.swift @@ -1,5 +1,6 @@ // RUN: %target-swift-frontend -experimental-performance-annotations -emit-sil %s -o /dev/null -verify // REQUIRES: swift_stdlib_no_asserts,optimized_stdlib +// REQUIRES: rdar90495704 protocol P { func protoMethod(_ a: Int) -> Int From 59b62c2cc9aab6a3497dcec11a3a83b8ea9d6460 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 18 Mar 2022 11:24:47 -0700 Subject: [PATCH 216/242] NFC: Update tests to include "before: " label in the @_backDeploy attribute. --- test/ModuleInterface/back-deploy-attr.swift | 36 ++--- .../back_deploy_attribute_accessor.swift | 2 +- test/SILGen/back_deploy_attribute_func.swift | 4 +- .../back_deploy_attribute_generic_func.swift | 2 +- .../back_deploy_attribute_struct_method.swift | 2 +- .../back_deploy_attribute_throwing_func.swift | 2 +- test/Sema/availability_define.swift | 2 +- test/attr/Inputs/BackDeployHelper.swift | 34 ++--- test/attr/attr_backDeploy.swift | 130 +++++++++--------- test/attr/attr_inlinable_available.swift | 2 +- test/attr/attr_objc.swift | 2 +- 11 files changed, 109 insertions(+), 109 deletions(-) diff --git a/test/ModuleInterface/back-deploy-attr.swift b/test/ModuleInterface/back-deploy-attr.swift index d9cee0834b44e..f18fe0f7820a5 100644 --- a/test/ModuleInterface/back-deploy-attr.swift +++ b/test/ModuleInterface/back-deploy-attr.swift @@ -18,73 +18,73 @@ // RUN: %FileCheck %s --check-prefix FROMMODULE --check-prefix CHECK < %t/TestFromModule.swiftinterface public struct TopLevelStruct { - // CHECK: @_backDeploy(macOS 12.0) + // CHECK: @_backDeploy(before: macOS 12.0) // FROMSOURCE: public func backDeployedFunc_SinglePlatform() -> Swift.Int { return 42 } // FROMMODULE: public func backDeployedFunc_SinglePlatform() -> Swift.Int @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public func backDeployedFunc_SinglePlatform() -> Int { return 42 } - // CHECK: @_backDeploy(macOS 12.0) - // CHECK: @_backDeploy(iOS 15.0) + // CHECK: @_backDeploy(before: macOS 12.0) + // CHECK: @_backDeploy(before: iOS 15.0) // FROMSOURCE: public func backDeployedFunc_MultiPlatform() -> Swift.Int { return 43 } // FROMMODULE: public func backDeployedFunc_MultiPlatform() -> Swift.Int @available(macOS 11.0, iOS 14.0, *) - @_backDeploy(macOS 12.0, iOS 15.0) + @_backDeploy(before: macOS 12.0, iOS 15.0) public func backDeployedFunc_MultiPlatform() -> Int { return 43 } - // CHECK: @_backDeploy(macOS 12.0) + // CHECK: @_backDeploy(before: macOS 12.0) // FROMSOURCE: public var backDeployedComputedProperty: Swift.Int { // FROMSOURCE: get { 44 } // FROMSOURCE: } // FROMMODULE: public var backDeployedComputedProperty: Swift.Int @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public var backDeployedComputedProperty: Int { 44 } - // CHECK: @_backDeploy(macOS 12.0) + // CHECK: @_backDeploy(before: macOS 12.0) // FROMSOURCE: public var backDeployedPropertyWithAccessors: Swift.Int { // FROMSOURCE: get { 45 } // FROMSOURCE: } // FROMMODULE: public var backDeployedPropertyWithAccessors: Swift.Int @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public var backDeployedPropertyWithAccessors: Int { get { 45 } } - // CHECK: @_backDeploy(macOS 12.0) + // CHECK: @_backDeploy(before: macOS 12.0) // FROMSOURCE: public subscript(index: Swift.Int) -> Swift.Int { // FROMSOURCE: get { 46 } // FROMSOURCE: } // FROMMODULE: public subscript(index: Swift.Int) -> Swift.Int @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public subscript(index: Int) -> Int { get { 46 } } } -// CHECK: @_backDeploy(macOS 12.0) +// CHECK: @_backDeploy(before: macOS 12.0) // FROMSOURCE: public func backDeployTopLevelFunc1() -> Swift.Int { return 47 } // FROMMODULE: public func backDeployTopLevelFunc1() -> Swift.Int @available(macOS 11.0, *) -@_backDeploy(macOS 12.0) +@_backDeploy(before: macOS 12.0) public func backDeployTopLevelFunc1() -> Int { return 47 } // MARK: - Availability macros -// CHECK: @_backDeploy(macOS 12.1) +// CHECK: @_backDeploy(before: macOS 12.1) // FROMSOURCE: public func backDeployTopLevelFunc2() -> Swift.Int { return 48 } // FROMMODULE: public func backDeployTopLevelFunc2() -> Swift.Int @available(macOS 11.0, *) -@_backDeploy(_macOS12_1) +@_backDeploy(before: _macOS12_1) public func backDeployTopLevelFunc2() -> Int { return 48 } -// CHECK: @_backDeploy(macOS 12.1) -// CHECK: @_backDeploy(iOS 15.1) +// CHECK: @_backDeploy(before: macOS 12.1) +// CHECK: @_backDeploy(before: iOS 15.1) // FROMSOURCE: public func backDeployTopLevelFunc3() -> Swift.Int { return 49 } // FROMMODULE: public func backDeployTopLevelFunc3() -> Swift.Int @available(macOS 11.0, iOS 14.0, *) -@_backDeploy(_myProject 1.0) +@_backDeploy(before: _myProject 1.0) public func backDeployTopLevelFunc3() -> Int { return 49 } diff --git a/test/SILGen/back_deploy_attribute_accessor.swift b/test/SILGen/back_deploy_attribute_accessor.swift index 0458e6a5bd367..d2aa336bbc757 100644 --- a/test/SILGen/back_deploy_attribute_accessor.swift +++ b/test/SILGen/back_deploy_attribute_accessor.swift @@ -38,7 +38,7 @@ public struct TopLevelStruct { // -- Original definition of TopLevelStruct.property.getter // CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy14TopLevelStructV8propertyACvg : $@convention(method) (TopLevelStruct) -> TopLevelStruct @available(macOS 10.51, *) - @_backDeploy(macOS 10.52) + @_backDeploy(before: macOS 10.52) public var property: TopLevelStruct { self } } diff --git a/test/SILGen/back_deploy_attribute_func.swift b/test/SILGen/back_deploy_attribute_func.swift index 6a1c2a8a6a0cc..76e245b6c475d 100644 --- a/test/SILGen/back_deploy_attribute_func.swift +++ b/test/SILGen/back_deploy_attribute_func.swift @@ -38,7 +38,7 @@ // -- Original definition of trivialFunc() // CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy11trivialFuncyyF : $@convention(thin) () -> () @available(macOS 10.51, *) -@_backDeploy(macOS 10.52) +@_backDeploy(before: macOS 10.52) public func trivialFunc() {} // -- Fallback definition of isNumber(_:) @@ -73,7 +73,7 @@ public func trivialFunc() {} // -- Original definition of isNumber(_:) // CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy8isNumberySbSiF : $@convention(thin) (Int) -> Bool @available(macOS 10.51, *) -@_backDeploy(macOS 10.52) +@_backDeploy(before: macOS 10.52) public func isNumber(_ x: Int) -> Bool { return true } diff --git a/test/SILGen/back_deploy_attribute_generic_func.swift b/test/SILGen/back_deploy_attribute_generic_func.swift index a9413848ebfd4..b4c0338b10b2b 100644 --- a/test/SILGen/back_deploy_attribute_generic_func.swift +++ b/test/SILGen/back_deploy_attribute_generic_func.swift @@ -39,7 +39,7 @@ // -- Original definition of genericFunc() // CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy11genericFuncyxxlF : $@convention(thin) (@in_guaranteed T) -> @out T @available(macOS 10.51, *) -@_backDeploy(macOS 10.52) +@_backDeploy(before: macOS 10.52) public func genericFunc(_ t: T) -> T { return t } diff --git a/test/SILGen/back_deploy_attribute_struct_method.swift b/test/SILGen/back_deploy_attribute_struct_method.swift index de19e5f3ced5a..9fc630a75904e 100644 --- a/test/SILGen/back_deploy_attribute_struct_method.swift +++ b/test/SILGen/back_deploy_attribute_struct_method.swift @@ -40,7 +40,7 @@ public struct TopLevelStruct { // -- Original definition of TopLevelStruct.trivialMethod() // CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy14TopLevelStructV13trivialMethodyyF : $@convention(method) (TopLevelStruct) -> () @available(macOS 10.51, *) - @_backDeploy(macOS 10.52) + @_backDeploy(before: macOS 10.52) public func trivialMethod() {} } diff --git a/test/SILGen/back_deploy_attribute_throwing_func.swift b/test/SILGen/back_deploy_attribute_throwing_func.swift index e16dca1d2f857..fd0753bc86bfc 100644 --- a/test/SILGen/back_deploy_attribute_throwing_func.swift +++ b/test/SILGen/back_deploy_attribute_throwing_func.swift @@ -51,7 +51,7 @@ // -- Original definition of throwingFunc() // CHECK-LABEL: sil [serialized] [available 10.51] [ossa] @$s11back_deploy12throwingFuncyyKF : $@convention(thin) () -> @error Error @available(macOS 10.51, *) -@_backDeploy(macOS 10.52) +@_backDeploy(before: macOS 10.52) public func throwingFunc() throws {} // CHECK-LABEL: sil hidden [available 10.51] [ossa] @$s11back_deploy6calleryyKF : $@convention(thin) () -> @error Error diff --git a/test/Sema/availability_define.swift b/test/Sema/availability_define.swift index 502db10867d7f..e9585bf3d6f20 100644 --- a/test/Sema/availability_define.swift +++ b/test/Sema/availability_define.swift @@ -87,7 +87,7 @@ public func forbidMacrosInInlinableCode1() { } @available(_iOS8Aligned, *) -@_backDeploy(_iOS9Aligned) +@_backDeploy(before: _iOS9Aligned) public func forbidMacrosInInlinableCode2() { if #available(_iOS9Aligned, *) { } // expected-error {{availability macro cannot be used in a '@_backDeploy' function}} if #available(_iOS9, _macOS10_11, *) { } // expected-error {{availability macro cannot be used in a '@_backDeploy' function}} diff --git a/test/attr/Inputs/BackDeployHelper.swift b/test/attr/Inputs/BackDeployHelper.swift index 022d2368aa0eb..dbc1c44a55b23 100644 --- a/test/attr/Inputs/BackDeployHelper.swift +++ b/test/attr/Inputs/BackDeployHelper.swift @@ -95,20 +95,20 @@ extension Int { #if !STRIP_V2_APIS @available(BackDeploy 1.0, *) -@_backDeploy(BackDeploy 2.0) +@_backDeploy(before: BackDeploy 2.0) public func trivial() { testPrint(handle: #dsohandle, "trivial") } @available(BackDeploy 1.0, *) -@_backDeploy(BackDeploy 2.0) +@_backDeploy(before: BackDeploy 2.0) public func pleaseThrow(_ shouldThrow: Bool) throws -> Bool { if shouldThrow { throw BadError.bad } return !shouldThrow } @available(BackDeploy 1.0, *) -@_backDeploy(BackDeploy 2.0) +@_backDeploy(before: BackDeploy 2.0) public func genericIncrement( _ x: inout T, by amount: T.Operand @@ -117,78 +117,78 @@ public func genericIncrement( } @available(BackDeploy 1.0, *) -@_backDeploy(BackDeploy 2.0) +@_backDeploy(before: BackDeploy 2.0) public func existentialIncrementByOne(_ x: inout any Incrementable) { testPrint(handle: #dsohandle, x.incrementByOne()) } extension MutableInt { @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public var value: Int { _value } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public func print() { // Tests recursive @_backDeploy since `value` is also @_backDeploy testPrint(handle: #dsohandle, String(value)) } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public static var zero: Self { MutableInt(0) } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public mutating func decrement(by amount: Int) -> Int { _value -= amount return _value } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public func toIncrementable() -> any Incrementable { self } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public subscript(byteAt index: Int) -> UInt8 { _value.byte(at: index) } } extension ReferenceInt { @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public final var value: Int { _value } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public final func print() { // Tests recursive use of back deployed APIs, since `value` is also testPrint(handle: #dsohandle, String(value)) } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public final func copy() -> ReferenceInt { return ReferenceInt(value) } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public final class var zero: ReferenceInt { ReferenceInt(0) } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public final func decrement(by amount: Int) -> Int { _value -= amount return _value } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public final func toIncrementable() -> any Incrementable { self } @available(BackDeploy 1.0, *) - @_backDeploy(BackDeploy 2.0) + @_backDeploy(before: BackDeploy 2.0) public final subscript(byteAt index: Int) -> UInt8 { _value.byte(at: index) } } diff --git a/test/attr/attr_backDeploy.swift b/test/attr/attr_backDeploy.swift index 35f80e5f44615..b4e50e86d1db2 100644 --- a/test/attr/attr_backDeploy.swift +++ b/test/attr/attr_backDeploy.swift @@ -5,57 +5,57 @@ // OK: top level functions @available(macOS 11.0, *) -@_backDeploy(macOS 12.0) +@_backDeploy(before: macOS 12.0) public func backDeployedTopLevelFunc() {} // OK: internal decls may be back deployed when @usableFromInline @available(macOS 11.0, *) -@_backDeploy(macOS 12.0) +@_backDeploy(before: macOS 12.0) @usableFromInline internal func backDeployedUsableFromInlineTopLevelFunc() {} // OK: function/property/subscript decls in a struct public struct TopLevelStruct { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public func backDeployedMethod() {} @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public var backDeployedComputedProperty: Int { 98 } @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public subscript(_ index: Int) -> Int { index } } // OK: final function decls in a non-final class public class TopLevelClass { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) final public func backDeployedFinalMethod() {} @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) final public var backDeployedFinalComputedProperty: Int { 98 } @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public static func backDeployedStaticMethod() {} @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public final class func backDeployedClassMethod() {} } // OK: function decls in a final class final public class FinalTopLevelClass { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public func backDeployedMethod() {} @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public var backDeployedComputedProperty: Int { 98 } } @@ -63,31 +63,31 @@ final public class FinalTopLevelClass { @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) public actor TopLevelActor { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) final public func finalActorMethod() {} // OK: actor methods are effectively final @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public func actorMethod() {} } // OK: function decls in extension on public types extension TopLevelStruct { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public func backDeployedExtensionMethod() {} } extension TopLevelClass { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) final public func backDeployedExtensionMethod() {} } extension FinalTopLevelClass { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public func backDeployedExtensionMethod() {} } @@ -95,54 +95,54 @@ public protocol TopLevelProtocol {} extension TopLevelProtocol { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public func backDeployedExtensionMethod() {} } // MARK: - Unsupported declaration types -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} public class CannotBackDeployClass {} -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} public struct CannotBackDeployStruct { - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' must not be used on stored properties}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' must not be used on stored properties}} public var cannotBackDeployStoredProperty: Int = 83 - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' must not be used on stored properties}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' must not be used on stored properties}} public lazy var cannotBackDeployLazyStoredProperty: Int = 15 } -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} public enum CannotBackDeployEnum { - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} case cannotBackDeployEnumCase } -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' must not be used on stored properties}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' must not be used on stored properties}} public var cannotBackDeployTopLevelVar = 79 -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} extension TopLevelStruct {} -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} protocol CannotBackDeployProtocol {} @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' attribute cannot be applied to this declaration}} public actor CannotBackDeployActor {} // FIXME(backDeploy): support coroutines rdar://90111169 public struct CannotBackDeployCoroutines { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' is not supported on coroutine _modify accessor}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' is not supported on coroutine _modify accessor}} public var readWriteProperty: Int { get { 42 } set(newValue) {} } @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' is not supported on coroutine _modify accessor}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' is not supported on coroutine _modify accessor}} public subscript(at index: Int) -> Int { get { 42 } set(newValue) {} @@ -150,11 +150,11 @@ public struct CannotBackDeployCoroutines { public var explicitReadAndModify: Int { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' is not supported on coroutine _read accessor}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' is not supported on coroutine _read accessor}} _read { yield 42 } @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' is not supported on coroutine _modify accessor}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' is not supported on coroutine _modify accessor}} _modify {} } } @@ -169,7 +169,7 @@ public struct FunctionBodyDiagnostics { private func privateFunc() {} // expected-note {{instance method 'privateFunc()' is not '@usableFromInline' or public}} @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) public func backDeployedMethod() { struct Nested {} // expected-error {{type 'Nested' cannot be nested inside a '@_backDeploy' function}} @@ -183,125 +183,125 @@ public struct FunctionBodyDiagnostics { // MARK: - Incompatible declarations -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' may not be used on fileprivate declarations}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' may not be used on fileprivate declarations}} fileprivate func filePrivateFunc() {} -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' may not be used on private declarations}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' may not be used on private declarations}} private func privateFunc() {} -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' may not be used on internal declarations}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' may not be used on internal declarations}} internal func internalFunc() {} private struct PrivateTopLevelStruct { - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' may not be used on private declarations}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' may not be used on private declarations}} public func effectivelyPrivateFunc() {} } public class TopLevelClass2 { - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' cannot be applied to a non-final instance method}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' cannot be applied to a non-final instance method}} public func nonFinalMethod() {} - @_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' cannot be applied to a non-final class method}} + @_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' cannot be applied to a non-final class method}} public class func nonFinalClassMethod() {} } -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' requires that 'missingAllAvailabilityFunc()' have explicit availability for macOS}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' requires that 'missingAllAvailabilityFunc()' have explicit availability for macOS}} public func missingAllAvailabilityFunc() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0, iOS 15.0) // expected-error {{'@_backDeploy' requires that 'missingiOSAvailabilityFunc()' have explicit availability for iOS}} +@_backDeploy(before: macOS 12.0, iOS 15.0) // expected-error {{'@_backDeploy' requires that 'missingiOSAvailabilityFunc()' have explicit availability for iOS}} public func missingiOSAvailabilityFunc() {} @available(macOS 12.0, *) -@_backDeploy(macOS 12.0) // expected-error {{'@_backDeploy' has no effect because 'availableSameVersionAsBackDeployment()' is not available before macOS 12.0}} +@_backDeploy(before: macOS 12.0) // expected-error {{'@_backDeploy' has no effect because 'availableSameVersionAsBackDeployment()' is not available before macOS 12.0}} public func availableSameVersionAsBackDeployment() {} @available(macOS 12.1, *) -@_backDeploy(macOS 12.0) // expected-error {{'availableAfterBackDeployment()' is not available before macOS 12.0}} +@_backDeploy(before: macOS 12.0) // expected-error {{'availableAfterBackDeployment()' is not available before macOS 12.0}} public func availableAfterBackDeployment() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0, macOS 13.0) // expected-error {{'@_backDeploy' contains multiple versions for macOS}} +@_backDeploy(before: macOS 12.0, macOS 13.0) // expected-error {{'@_backDeploy' contains multiple versions for macOS}} public func duplicatePlatformsFunc1() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0) -@_backDeploy(macOS 13.0) // expected-error {{'@_backDeploy' contains multiple versions for macOS}} +@_backDeploy(before: macOS 12.0) +@_backDeploy(before: macOS 13.0) // expected-error {{'@_backDeploy' contains multiple versions for macOS}} public func duplicatePlatformsFunc2() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0) +@_backDeploy(before: macOS 12.0) @_alwaysEmitIntoClient // expected-error {{'@_alwaysEmitIntoClient' cannot be applied to a back deployed global function}} public func alwaysEmitIntoClientFunc() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0) +@_backDeploy(before: macOS 12.0) @inlinable // expected-error {{'@inlinable' cannot be applied to a back deployed global function}} public func inlinableFunc() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0) +@_backDeploy(before: macOS 12.0) @_transparent // expected-error {{'@_transparent' cannot be applied to a back deployed global function}} public func transparentFunc() {} // MARK: - Attribute parsing @available(macOS 11.0, *) -@_backDeploy(macOS 12.0, unknownOS 1.0) // expected-warning {{unknown platform 'unknownOS' for attribute '@_backDeploy'}} +@_backDeploy(before: macOS 12.0, unknownOS 1.0) // expected-warning {{unknown platform 'unknownOS' for attribute '@_backDeploy'}} public func unknownOSFunc() {} -@_backDeploy(@) // expected-error {{expected platform in '@_backDeploy' attribute}} +@_backDeploy(before: @) // expected-error {{expected platform in '@_backDeploy' attribute}} public func badPlatformFunc1() {} -@_backDeploy(@ 12.0) // expected-error {{expected platform in '@_backDeploy' attribute}} +@_backDeploy(before: @ 12.0) // expected-error {{expected platform in '@_backDeploy' attribute}} public func badPlatformFunc2() {} -@_backDeploy(macOS) // expected-error {{expected version number in '@_backDeploy' attribute}} +@_backDeploy(before: macOS) // expected-error {{expected version number in '@_backDeploy' attribute}} public func missingVersionFunc1() {} -@_backDeploy(macOS 12.0, iOS) // expected-error {{expected version number in '@_backDeploy' attribute}} +@_backDeploy(before: macOS 12.0, iOS) // expected-error {{expected version number in '@_backDeploy' attribute}} public func missingVersionFunc2() {} -@_backDeploy(macOS, iOS) // expected-error 2{{expected version number in '@_backDeploy' attribute}} +@_backDeploy(before: macOS, iOS) // expected-error 2{{expected version number in '@_backDeploy' attribute}} public func missingVersionFunc3() {} @available(macOS 11.0, iOS 14.0, *) -@_backDeploy(macOS 12.0, iOS 15.0,) // expected-error {{unexpected ',' separator}} +@_backDeploy(before: macOS 12.0, iOS 15.0,) // expected-error {{unexpected ',' separator}} public func unexpectedSeparatorFunc() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0.1) // expected-warning {{'@_backDeploy' only uses major and minor version number}} +@_backDeploy(before: macOS 12.0.1) // expected-warning {{'@_backDeploy' only uses major and minor version number}} public func patchVersionFunc() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0, * 9.0) // expected-warning {{* as platform name has no effect in '@_backDeploy' attribute}} +@_backDeploy(before: macOS 12.0, * 9.0) // expected-warning {{* as platform name has no effect in '@_backDeploy' attribute}} public func wildcardWithVersionFunc() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0, *) // expected-warning {{* as platform name has no effect in '@_backDeploy' attribute}} +@_backDeploy(before: macOS 12.0, *) // expected-warning {{* as platform name has no effect in '@_backDeploy' attribute}} public func trailingWildcardFunc() {} @available(macOS 11.0, iOS 14.0, *) -@_backDeploy(macOS 12.0, *, iOS 15.0) // expected-warning {{* as platform name has no effect in '@_backDeploy' attribute}} +@_backDeploy(before: macOS 12.0, *, iOS 15.0) // expected-warning {{* as platform name has no effect in '@_backDeploy' attribute}} public func embeddedWildcardFunc() {} -@_backDeploy(_myProject 3.0) // expected-error {{reference to undefined version '3.0' for availability macro '_myProject'}} +@_backDeploy(before: _myProject 3.0) // expected-error {{reference to undefined version '3.0' for availability macro '_myProject'}} public func macroVersionned() {} -@_backDeploy(_myProject) // expected-error {{reference to undefined version '0' for availability macro '_myProject'}} +@_backDeploy(before: _myProject) // expected-error {{reference to undefined version '0' for availability macro '_myProject'}} public func missingMacroVersion() {} // Fall back to the default diagnostic when the macro is unknown. -@_backDeploy(_unknownMacro) // expected-warning {{unknown platform '_unknownMacro' for attribute '@_backDeploy'}} +@_backDeploy(before: _unknownMacro) // expected-warning {{unknown platform '_unknownMacro' for attribute '@_backDeploy'}} // expected-error@-1 {{expected version number in '@_backDeploy' attribute}} public func unknownMacroMissingVersion() {} -@_backDeploy(_unknownMacro 1.0) // expected-warning {{unknown platform '_unknownMacro' for attribute '@_backDeploy'}} +@_backDeploy(before: _unknownMacro 1.0) // expected-warning {{unknown platform '_unknownMacro' for attribute '@_backDeploy'}} // expected-error@-1 {{expected at least one platform version in '@_backDeploy' attribute}} public func unknownMacroVersionned() {} @available(macOS 11.0, *) -@_backDeploy(_unknownMacro 1.0, _myProject 2.0) // expected-warning {{unknown platform '_unknownMacro' for attribute '@_backDeploy'}} +@_backDeploy(before: _unknownMacro 1.0, _myProject 2.0) // expected-warning {{unknown platform '_unknownMacro' for attribute '@_backDeploy'}} public func knownAndUnknownMacroVersionned() {} @_backDeploy() // expected-error {{expected at least one platform version in '@_backDeploy' attribute}} @@ -310,5 +310,5 @@ public func emptyPlatformVersionsFunc() {} @_backDeploy // expected-error {{expected '(' in '_backDeploy' attribute}} public func expectedLeftParenFunc() {} -@_backDeploy(macOS 12.0 // expected-note {{to match this opening '('}} +@_backDeploy(before: macOS 12.0 // expected-note {{to match this opening '('}} public func expectedRightParenFunc() {} // expected-error {{expected ')' in '@_backDeploy' argument list}} diff --git a/test/attr/attr_inlinable_available.swift b/test/attr/attr_inlinable_available.swift index 74ac804c3ced0..9126954b1c8da 100644 --- a/test/attr/attr_inlinable_available.swift +++ b/test/attr/attr_inlinable_available.swift @@ -428,7 +428,7 @@ internal func fn() { // @_backDeploy acts like @inlinable. @available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) -@_backDeploy(macOS 999.0, iOS 999.0, tvOS 999.0, watchOS 999.0) +@_backDeploy(before: macOS 999.0, iOS 999.0, tvOS 999.0, watchOS 999.0) public func backDeployedToInliningTarget( _: NoAvailable, _: BeforeInliningTarget, diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index a0125eca80c08..bb032e9421b8c 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -2660,7 +2660,7 @@ class SR12801 { public class BackDeployClass { @available(macOS 11.0, *) - @_backDeploy(macOS 12.0) + @_backDeploy(before: macOS 12.0) @objc // expected-error {{'@objc' cannot be applied to a back deployed instance method}} final public func objcMethod() {} } From 2a646dc4381b178232344e42f2dbe9e7249073ea Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 18 Mar 2022 11:30:51 -0700 Subject: [PATCH 217/242] Parse: Require a "before: " label in the first item of the list in the @_backDeploy attribute in order to match the pitched syntax for the attribute. Refactor existing comma separated list parsing code to take advantage of part of it in the attribute parsing. --- docs/ReferenceGuides/UnderscoredAttributes.md | 17 ++- include/swift/AST/DiagnosticsParse.def | 4 + include/swift/Parse/Parser.h | 20 +++ lib/AST/Attr.cpp | 2 +- lib/Parse/ParseDecl.cpp | 109 +++++++++------ lib/Parse/Parser.cpp | 126 ++++++++++-------- test/attr/attr_backDeploy.swift | 28 +++- utils/gyb_syntax_support/AttributeNodes.py | 44 ++++++ .../NodeSerializationCodes.py | 3 + 9 files changed, 250 insertions(+), 103 deletions(-) diff --git a/docs/ReferenceGuides/UnderscoredAttributes.md b/docs/ReferenceGuides/UnderscoredAttributes.md index 8c7a5c668a02f..11bd3d3e1453e 100644 --- a/docs/ReferenceGuides/UnderscoredAttributes.md +++ b/docs/ReferenceGuides/UnderscoredAttributes.md @@ -39,14 +39,19 @@ Most notably, default argument expressions are implicitly `@_alwaysEmitIntoClient`, which means that adding a default argument to a function which did not have one previously does not break ABI. -## `@_backDeploy(availabilitySpec ...)` +## `@_backDeploy(before: ...)` Causes the body of a function to be emitted into the module interface to be -available for inlining in clients with deployment targets lower than the formal -availability of the function. When inlined, the body of the function is -transformed such that it calls the library's copy of the function if it is -available at runtime. Otherwise, the copy of the original function body is -executed. +available for emission into clients with deployment targets lower than the +ABI availability of the function. When the client's deployment target is +before the function's ABI availability, the compiler replaces calls to that +function with a call to a thunk that checks at runtime whether the original +library function is available. If the the original is available then it is +called. Otherwise, the fallback copy of the function that was emitted into the +client is called instead. + +For more details, see the [pitch thread](https://forums.swift.org/t/pitch-function-back-deployment/55769/) +in the forums. ## `@_assemblyVision` diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index a8056baeb0c1d..834854bbd1a97 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1572,6 +1572,10 @@ ERROR(originally_defined_in_need_nonempty_module_name,none, "original module name cannot be empty in @_originallyDefinedIn", ()) // backDeploy +ERROR(attr_back_deploy_expected_before_label,none, + "expected 'before:' in '@_backDeploy' attribute", ()) +ERROR(attr_back_deploy_expected_colon_after_before,none, + "expected ':' after 'before' in '@_backDeploy' attribute", ()) ERROR(attr_back_deploy_missing_rparen,none, "expected ')' in '@_backDeploy' argument list", ()) diff --git a/include/swift/Parse/Parser.h b/include/swift/Parse/Parser.h index 8bea18fbd839b..78357e240be38 100644 --- a/include/swift/Parse/Parser.h +++ b/include/swift/Parse/Parser.h @@ -895,6 +895,22 @@ class Parser { /// close brace. SourceLoc getErrorOrMissingLoc() const; + enum class ParseListItemResult { + /// There are more list items to parse. + Continue, + /// The list ended inside a string literal interpolation context. + FinishedInStringInterpolation, + /// The list ended for another reason. + Finished, + }; + + /// Parses a single item from a comma separated list and updates `Status`. + ParseListItemResult + parseListItem(ParserStatus &Status, tok RightK, SourceLoc LeftLoc, + SourceLoc &RightLoc, bool AllowSepAfterLast, + SyntaxKind ElementKind, + llvm::function_ref callback); + /// Parse a comma separated list of some elements. ParserStatus parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, bool AllowSepAfterLast, Diag<> ErrorDiag, @@ -1070,6 +1086,10 @@ class Parser { ParserResult parseTransposeAttribute(SourceLoc AtLoc, SourceLoc Loc); + /// Parse the @_backDeploy attribute. + bool parseBackDeployAttribute(DeclAttributes &Attributes, StringRef AttrName, + SourceLoc AtLoc, SourceLoc Loc); + /// Parse a specific attribute. ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, PatternBindingInitializer *&initContext, diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 75767417fbb23..8df6cc0215bad 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1215,7 +1215,7 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, case DAK_BackDeploy: { Printer.printAttrName("@_backDeploy"); - Printer << "("; + Printer << "(before: "; auto Attr = cast(this); Printer << platformString(Attr->Platform) << " " << Attr->Version.getAsString(); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 07d011ea37409..6c5a29369fb0b 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1824,6 +1824,76 @@ ParserStatus Parser::parsePlatformVersionInList(StringRef AttrName, return makeParserSuccess(); } +bool Parser::parseBackDeployAttribute(DeclAttributes &Attributes, + StringRef AttrName, SourceLoc AtLoc, + SourceLoc Loc) { + std::string AtAttrName = (llvm::Twine("@") + AttrName).str(); + auto LeftLoc = Tok.getLoc(); + if (!consumeIf(tok::l_paren)) { + diagnose(Loc, diag::attr_expected_lparen, AtAttrName, + DeclAttribute::isDeclModifier(DAK_BackDeploy)); + return false; + } + + SourceLoc RightLoc; + ParserStatus Status; + bool SuppressLaterDiags = false; + llvm::SmallVector PlatformAndVersions; + + { + SyntaxParsingContext SpecListListContext( + SyntaxContext, SyntaxKind::BackDeployAttributeSpecList); + + // Parse 'before' ':'. + if (Tok.is(tok::identifier) && Tok.getText() == "before") { + consumeToken(); + if (!consumeIf(tok::colon)) + diagnose(Tok, diag::attr_back_deploy_expected_colon_after_before); + } else { + diagnose(Tok, diag::attr_back_deploy_expected_before_label); + } + + // Parse the version list. + if (!Tok.is(tok::r_paren)) { + SyntaxParsingContext VersionListContext( + SyntaxContext, SyntaxKind::BackDeployVersionList); + + ParseListItemResult Result; + do { + Result = parseListItem(Status, tok::r_paren, LeftLoc, RightLoc, + /*AllowSepAfterLast=*/false, + SyntaxKind::BackDeployVersionArgument, + [&]() -> ParserStatus { + return parsePlatformVersionInList( + AtAttrName, PlatformAndVersions); + }); + } while (Result == ParseListItemResult::Continue); + } + } + + if (parseMatchingToken(tok::r_paren, RightLoc, + diag::attr_back_deploy_missing_rparen, LeftLoc)) + return false; + + if (Status.isErrorOrHasCompletion() || SuppressLaterDiags) { + return false; + } + + if (PlatformAndVersions.empty()) { + diagnose(Loc, diag::attr_availability_need_platform_version, AtAttrName); + return false; + } + + assert(!PlatformAndVersions.empty()); + auto AttrRange = SourceRange(Loc, Tok.getLoc()); + for (auto &Item : PlatformAndVersions) { + Attributes.add(new (Context) + BackDeployAttr(AtLoc, AttrRange, Item.first, Item.second, + /*IsImplicit*/ false)); + } + return true; +} + /// Processes a parsed option name by attempting to match it to a list of /// alternative name/value pairs provided by a chain of \c when() calls, ending /// in either \c whenOmitted() if omitting the option is allowed, or @@ -2858,45 +2928,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, break; } case DAK_BackDeploy: { - auto LeftLoc = Tok.getLoc(); - if (!consumeIf(tok::l_paren)) { - diagnose(Loc, diag::attr_expected_lparen, AttrName, - DeclAttribute::isDeclModifier(DK)); - return false; - } - - bool SuppressLaterDiags = false; - SourceLoc RightLoc; - llvm::SmallVector PlatformAndVersions; - StringRef AttrName = "@_backDeploy"; - ParserStatus Status = parseList(tok::r_paren, LeftLoc, RightLoc, false, - diag::attr_back_deploy_missing_rparen, - SyntaxKind::AvailabilitySpecList, - [&]() -> ParserStatus { - ParserStatus ListItemStatus = - parsePlatformVersionInList(AttrName, PlatformAndVersions); - if (ListItemStatus.isErrorOrHasCompletion()) - SuppressLaterDiags = true; - return ListItemStatus; - }); - - if (Status.isErrorOrHasCompletion() || SuppressLaterDiags) { - return false; - } - - if (PlatformAndVersions.empty()) { - diagnose(Loc, diag::attr_availability_need_platform_version, AttrName); + if (!parseBackDeployAttribute(Attributes, AttrName, AtLoc, Loc)) return false; - } - - assert(!PlatformAndVersions.empty()); - AttrRange = SourceRange(Loc, Tok.getLoc()); - for (auto &Item: PlatformAndVersions) { - Attributes.add(new (Context) BackDeployAttr(AtLoc, AttrRange, - Item.first, - Item.second, - /*IsImplicit*/false)); - } break; } } diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 708a3858e8d40..4ef7ea8dea1f6 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1061,14 +1061,72 @@ static SyntaxKind getListElementKind(SyntaxKind ListKind) { } } +static bool tokIsStringInterpolationEOF(Token &Tok, tok RightK) { + return Tok.is(tok::eof) && Tok.getText() == ")" && RightK == tok::r_paren; +} + +Parser::ParseListItemResult +Parser::parseListItem(ParserStatus &Status, tok RightK, SourceLoc LeftLoc, + SourceLoc &RightLoc, bool AllowSepAfterLast, + SyntaxKind ElementKind, + llvm::function_ref callback) { + while (Tok.is(tok::comma)) { + diagnose(Tok, diag::unexpected_separator, ",").fixItRemove(Tok.getLoc()); + consumeToken(); + } + SourceLoc StartLoc = Tok.getLoc(); + + SyntaxParsingContext ElementContext(SyntaxContext, ElementKind); + if (ElementKind == SyntaxKind::Unknown) + ElementContext.setTransparent(); + Status |= callback(); + if (Tok.is(RightK)) + return ParseListItemResult::Finished; + + // If the lexer stopped with an EOF token whose spelling is ")", then this + // is actually the tuple that is a string literal interpolation context. + // Just accept the ")" and build the tuple as we usually do. + if (tokIsStringInterpolationEOF(Tok, RightK)) { + RightLoc = Tok.getLoc(); + return ParseListItemResult::FinishedInStringInterpolation; + } + // If we haven't made progress, or seeing any error, skip ahead. + if (Tok.getLoc() == StartLoc || Status.isErrorOrHasCompletion()) { + assert(Status.isErrorOrHasCompletion() && "no progress without error"); + skipListUntilDeclRBrace(LeftLoc, RightK, tok::comma); + if (Tok.is(RightK) || Tok.isNot(tok::comma)) + return ParseListItemResult::Finished; + } + if (consumeIf(tok::comma)) { + if (Tok.isNot(RightK)) + return ParseListItemResult::Continue; + if (!AllowSepAfterLast) { + diagnose(Tok, diag::unexpected_separator, ",").fixItRemove(PreviousLoc); + } + return ParseListItemResult::Finished; + } + // If we're in a comma-separated list, the next token is at the + // beginning of a new line and can never start an element, break. + if (Tok.isAtStartOfLine() && + (Tok.is(tok::r_brace) || isStartOfSwiftDecl() || isStartOfStmt())) { + return ParseListItemResult::Finished; + } + // If we found EOF or such, bailout. + if (Tok.isAny(tok::eof, tok::pound_endif)) { + IsInputIncomplete = true; + return ParseListItemResult::Finished; + } + + diagnose(Tok, diag::expected_separator, ",") + .fixItInsertAfter(PreviousLoc, ","); + Status.setIsParseError(); + return ParseListItemResult::Continue; +} + ParserStatus Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, bool AllowSepAfterLast, Diag<> ErrorDiag, SyntaxKind Kind, llvm::function_ref callback) { - auto TokIsStringInterpolationEOF = [&]() -> bool { - return Tok.is(tok::eof) && Tok.getText() == ")" && RightK == tok::r_paren; - }; - llvm::Optional ListContext; ListContext.emplace(SyntaxContext, Kind); if (Kind == SyntaxKind::Unknown) @@ -1081,64 +1139,20 @@ Parser::parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, RightLoc = consumeToken(RightK); return makeParserSuccess(); } - if (TokIsStringInterpolationEOF()) { + if (tokIsStringInterpolationEOF(Tok, RightK)) { RightLoc = Tok.getLoc(); return makeParserSuccess(); } ParserStatus Status; - while (true) { - while (Tok.is(tok::comma)) { - diagnose(Tok, diag::unexpected_separator, ",") - .fixItRemove(Tok.getLoc()); - consumeToken(); - } - SourceLoc StartLoc = Tok.getLoc(); - - SyntaxParsingContext ElementContext(SyntaxContext, ElementKind); - if (ElementKind == SyntaxKind::Unknown) - ElementContext.setTransparent(); - Status |= callback(); - if (Tok.is(RightK)) - break; - // If the lexer stopped with an EOF token whose spelling is ")", then this - // is actually the tuple that is a string literal interpolation context. - // Just accept the ")" and build the tuple as we usually do. - if (TokIsStringInterpolationEOF()) { - RightLoc = Tok.getLoc(); - return Status; - } - // If we haven't made progress, or seeing any error, skip ahead. - if (Tok.getLoc() == StartLoc || Status.isErrorOrHasCompletion()) { - assert(Status.isErrorOrHasCompletion() && "no progress without error"); - skipListUntilDeclRBrace(LeftLoc, RightK, tok::comma); - if (Tok.is(RightK) || Tok.isNot(tok::comma)) - break; - } - if (consumeIf(tok::comma)) { - if (Tok.isNot(RightK)) - continue; - if (!AllowSepAfterLast) { - diagnose(Tok, diag::unexpected_separator, ",") - .fixItRemove(PreviousLoc); - } - break; - } - // If we're in a comma-separated list, the next token is at the - // beginning of a new line and can never start an element, break. - if (Tok.isAtStartOfLine() && - (Tok.is(tok::r_brace) || isStartOfSwiftDecl() || isStartOfStmt())) { - break; - } - // If we found EOF or such, bailout. - if (Tok.isAny(tok::eof, tok::pound_endif)) { - IsInputIncomplete = true; - break; - } + ParseListItemResult Result; + do { + Result = parseListItem(Status, RightK, LeftLoc, RightLoc, AllowSepAfterLast, + ElementKind, callback); + } while (Result == ParseListItemResult::Continue); - diagnose(Tok, diag::expected_separator, ",") - .fixItInsertAfter(PreviousLoc, ","); - Status.setIsParseError(); + if (Result == ParseListItemResult::FinishedInStringInterpolation) { + return Status; } ListContext.reset(); diff --git a/test/attr/attr_backDeploy.swift b/test/attr/attr_backDeploy.swift index b4e50e86d1db2..465c9f89b8814 100644 --- a/test/attr/attr_backDeploy.swift +++ b/test/attr/attr_backDeploy.swift @@ -304,10 +304,34 @@ public func unknownMacroVersionned() {} @_backDeploy(before: _unknownMacro 1.0, _myProject 2.0) // expected-warning {{unknown platform '_unknownMacro' for attribute '@_backDeploy'}} public func knownAndUnknownMacroVersionned() {} -@_backDeploy() // expected-error {{expected at least one platform version in '@_backDeploy' attribute}} +@_backDeploy() // expected-error {{expected 'before:' in '@_backDeploy' attribute}} +// expected-error@-1 {{expected at least one platform version in '@_backDeploy' attribute}} +public func emptyAttributeFunc() {} + +@available(macOS 11.0, *) +@_backDeploy(macOS 12.0) // expected-error {{expected 'before:' in '@_backDeploy' attribute}} +public func missingBeforeFunc() {} + +@_backDeploy(before) // expected-error {{expected ':' after 'before' in '@_backDeploy' attribute}} +// expected-error@-1 {{expected at least one platform version in '@_backDeploy' attribute}} +public func missingColonAfterBeforeFunc() {} + +@available(macOS 11.0, *) +@_backDeploy(before macOS 12.0) // expected-error {{expected ':' after 'before' in '@_backDeploy' attribute}} +public func missingColonBetweenBeforeAndPlatformFunc() {} + +@available(macOS 11.0, *) +@_backDeploy(before: macOS 12.0,) // expected-error {{unexpected ',' separator}} +public func unexpectedTrailingCommaFunc() {} + +@available(macOS 11.0, iOS 14.0, *) +@_backDeploy(before: macOS 12.0,, iOS 15.0) // expected-error {{unexpected ',' separator}} +public func extraCommaFunc() {} + +@_backDeploy(before:) // expected-error {{expected at least one platform version in '@_backDeploy' attribute}} public func emptyPlatformVersionsFunc() {} -@_backDeploy // expected-error {{expected '(' in '_backDeploy' attribute}} +@_backDeploy // expected-error {{expected '(' in '@_backDeploy' attribute}} public func expectedLeftParenFunc() {} @_backDeploy(before: macOS 12.0 // expected-note {{to match this opening '('}} diff --git a/utils/gyb_syntax_support/AttributeNodes.py b/utils/gyb_syntax_support/AttributeNodes.py index 43561f2daf099..2f91125d76f7e 100644 --- a/utils/gyb_syntax_support/AttributeNodes.py +++ b/utils/gyb_syntax_support/AttributeNodes.py @@ -35,6 +35,7 @@ # | specialize-attr-spec-list # | implements-attr-arguments # | named-attribute-string-argument + # | back-deploy-attr-spec-list # )? ')'? Node('Attribute', kind='Syntax', description=''' @@ -66,6 +67,8 @@ kind='DerivativeRegistrationAttributeArguments'), Child('NamedAttributeString', kind='NamedAttributeStringArgument'), + Child('BackDeployArguments', + kind='BackDeployAttributeSpecList'), # TokenList for custom effects which are parsed by # `FunctionEffects.parse()` in swift. Child('TokenList', kind='TokenList', @@ -416,4 +419,45 @@ specified. '''), ]), + + # The arguments of '@_backDeploy(...)' + # back-deploy-attr-spec-list -> 'before' ':' back-deploy-version-list + Node('BackDeployAttributeSpecList', kind='Syntax', + description=''' + A collection of arguments for the `@_backDeploy` attribute + ''', + children=[ + Child('BeforeLabel', kind='IdentifierToken', + text_choices=['before'], description='The "before" label.'), + Child('Colon', kind='ColonToken', description=''' + The colon separating "before" and the parameter list. + '''), + Child('VersionList', kind='BackDeployVersionList', + collection_element_name='Availability', description=''' + The list of OS versions in which the declaration became ABI + stable. + '''), + ]), + + # back-deploy-version-list -> + # back-deploy-version-entry back-deploy-version-list? + Node('BackDeployVersionList', kind='SyntaxCollection', + element='BackDeployVersionArgument'), + + # back-deploy-version-entry -> availability-version-restriction ','? + Node('BackDeployVersionArgument', kind='Syntax', + description=''' + A single platform/version pair in a `@_backDeploy` attribute, + e.g. `iOS 10.1`. + ''', + children=[ + Child('AvailabilityVersionRestriction', + kind='AvailabilityVersionRestriction'), + Child('TrailingComma', kind='CommaToken', is_optional=True, + description=''' + A trailing comma if the argument is followed by another + argument + '''), + ]), + ] diff --git a/utils/gyb_syntax_support/NodeSerializationCodes.py b/utils/gyb_syntax_support/NodeSerializationCodes.py index 19e721710c610..3f69515a65031 100644 --- a/utils/gyb_syntax_support/NodeSerializationCodes.py +++ b/utils/gyb_syntax_support/NodeSerializationCodes.py @@ -258,6 +258,9 @@ 'PrimaryAssociatedTypeList' : 254, 'PrimaryAssociatedType' : 255, 'PrimaryAssociatedTypeClause' : 256, + 'BackDeployAttributeSpecList' : 257, + 'BackDeployVersionList' : 258, + 'BackDeployVersionArgument' : 259, } From b93717cbaec476fb52dfe96e75cb06aebf17332f Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 18 Mar 2022 00:42:20 -0400 Subject: [PATCH 218/242] RequirementMachine: Enable flags directly in LangOptions This enables requirement machine minimization for all tools (SourceKit, etc) not just the frontend. --- include/swift/Basic/LangOptions.h | 6 +++--- lib/Frontend/CompilerInvocation.cpp | 4 ---- test/IDE/print_ast_tc_decls.swift | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index de10fee8ec98d..9c147c093bcff 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -528,17 +528,17 @@ namespace swift { /// Enable the new experimental protocol requirement signature minimization /// algorithm. RequirementMachineMode RequirementMachineProtocolSignatures = - RequirementMachineMode::Disabled; + RequirementMachineMode::Verify; /// Enable the new experimental generic signature minimization algorithm /// for abstract generic signatures. RequirementMachineMode RequirementMachineAbstractSignatures = - RequirementMachineMode::Disabled; + RequirementMachineMode::Verify; /// Enable the new experimental generic signature minimization algorithm /// for user-written generic signatures. RequirementMachineMode RequirementMachineInferredSignatures = - RequirementMachineMode::Disabled; + RequirementMachineMode::Verify; /// Disable preprocessing pass to eliminate conformance requirements /// on generic parameters which are made concrete. Usually you want this diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 5ea2f23bbdf33..8f6978be44907 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -912,10 +912,6 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.DisableSubstSILFunctionTypes = Args.hasArg(OPT_disable_subst_sil_function_types); - Opts.RequirementMachineProtocolSignatures = RequirementMachineMode::Verify; - Opts.RequirementMachineInferredSignatures = RequirementMachineMode::Verify; - Opts.RequirementMachineAbstractSignatures = RequirementMachineMode::Verify; - if (auto A = Args.getLastArg(OPT_requirement_machine_protocol_signatures_EQ)) { auto value = llvm::StringSwitch>(A->getValue()) .Case("off", RequirementMachineMode::Disabled) diff --git a/test/IDE/print_ast_tc_decls.swift b/test/IDE/print_ast_tc_decls.swift index 8c55907eaa172..47349c5bfac63 100644 --- a/test/IDE/print_ast_tc_decls.swift +++ b/test/IDE/print_ast_tc_decls.swift @@ -1231,7 +1231,7 @@ extension GenericParams1 where StructGenericBaz: FooProtocol { // PASS_PRINT_AST: static func contextualWhereClause2() where StructGenericBaz : FooClass{{$}} typealias ContextualWhereClause3 = Never where StructGenericBaz: QuxProtocol, StructGenericBaz.Qux == Void - // PASS_PRINT_AST: typealias ContextualWhereClause3 = Never where StructGenericBaz : QuxProtocol, StructGenericBaz.Qux == Void{{$}} + // PASS_PRINT_AST: typealias ContextualWhereClause3 = Never where StructGenericBaz : QuxProtocol, StructGenericBaz.Qux == (){{$}} } struct GenericParams2 where T : BarProtocol {} From 0c5cc386151349259a7bf5b590b47a5cf1dafbb4 Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Fri, 18 Mar 2022 12:15:28 -0700 Subject: [PATCH 219/242] Parse: Add fix-its for 'before:' label parse errors in the @_backDeploy attribute. --- lib/Parse/ParseDecl.cpp | 6 ++++-- test/attr/attr_backDeploy.swift | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 6c5a29369fb0b..3cebd21e9a156 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -1848,9 +1848,11 @@ bool Parser::parseBackDeployAttribute(DeclAttributes &Attributes, if (Tok.is(tok::identifier) && Tok.getText() == "before") { consumeToken(); if (!consumeIf(tok::colon)) - diagnose(Tok, diag::attr_back_deploy_expected_colon_after_before); + diagnose(Tok, diag::attr_back_deploy_expected_colon_after_before) + .fixItInsertAfter(PreviousLoc, ":"); } else { - diagnose(Tok, diag::attr_back_deploy_expected_before_label); + diagnose(Tok, diag::attr_back_deploy_expected_before_label) + .fixItInsertAfter(PreviousLoc, "before:"); } // Parse the version list. diff --git a/test/attr/attr_backDeploy.swift b/test/attr/attr_backDeploy.swift index 465c9f89b8814..f48d0485840ea 100644 --- a/test/attr/attr_backDeploy.swift +++ b/test/attr/attr_backDeploy.swift @@ -309,23 +309,23 @@ public func knownAndUnknownMacroVersionned() {} public func emptyAttributeFunc() {} @available(macOS 11.0, *) -@_backDeploy(macOS 12.0) // expected-error {{expected 'before:' in '@_backDeploy' attribute}} +@_backDeploy(macOS 12.0) // expected-error {{expected 'before:' in '@_backDeploy' attribute}} {{14-14=before:}} public func missingBeforeFunc() {} -@_backDeploy(before) // expected-error {{expected ':' after 'before' in '@_backDeploy' attribute}} +@_backDeploy(before) // expected-error {{expected ':' after 'before' in '@_backDeploy' attribute}} {{20-20=:}} // expected-error@-1 {{expected at least one platform version in '@_backDeploy' attribute}} public func missingColonAfterBeforeFunc() {} @available(macOS 11.0, *) -@_backDeploy(before macOS 12.0) // expected-error {{expected ':' after 'before' in '@_backDeploy' attribute}} +@_backDeploy(before macOS 12.0) // expected-error {{expected ':' after 'before' in '@_backDeploy' attribute}} {{20-20=:}} public func missingColonBetweenBeforeAndPlatformFunc() {} @available(macOS 11.0, *) -@_backDeploy(before: macOS 12.0,) // expected-error {{unexpected ',' separator}} +@_backDeploy(before: macOS 12.0,) // expected-error {{unexpected ',' separator}} {{32-33=}} public func unexpectedTrailingCommaFunc() {} @available(macOS 11.0, iOS 14.0, *) -@_backDeploy(before: macOS 12.0,, iOS 15.0) // expected-error {{unexpected ',' separator}} +@_backDeploy(before: macOS 12.0,, iOS 15.0) // expected-error {{unexpected ',' separator}} {{33-34=}} public func extraCommaFunc() {} @_backDeploy(before:) // expected-error {{expected at least one platform version in '@_backDeploy' attribute}} From ae2b5140edf87a0c5b73fd255ebaa937bed57d27 Mon Sep 17 00:00:00 2001 From: Mike Ash Date: Thu, 17 Mar 2022 11:48:07 -0400 Subject: [PATCH 220/242] [RemoteMirror][swift-inspect] Decode locks in priority-escalation concurrency runtime. When possible, decode the DrainLock/ExecutionLock fields of tasks and actors in concurrency runtimes built with priority escalation, and show the corresponding thread info in swift-inspect output. We weren't properly decoding actor flags previously, so fix that up as well and have Remote Mirror split them out into separate fields so clients don't have to. We were missing the Job Storage field from the definition of DefaultActorImpl in RuntimeInternals.h, fix that so we actually read the right data. rdar://88598003 --- include/swift/Concurrency/Actor.h | 71 +++++++++ include/swift/Reflection/ReflectionContext.h | 81 +++++++++- include/swift/Reflection/RuntimeInternals.h | 1 + .../SwiftRemoteMirrorTypes.h | 33 ++-- stdlib/public/Concurrency/Actor.cpp | 141 +++++++++--------- .../SwiftRemoteMirror/SwiftRemoteMirror.cpp | 17 ++- .../swift-inspect/DarwinRemoteProcess.swift | 128 +++++++++++----- .../Operations/DumpConcurrency.swift | 95 ++++++------ 8 files changed, 391 insertions(+), 176 deletions(-) create mode 100644 include/swift/Concurrency/Actor.h diff --git a/include/swift/Concurrency/Actor.h b/include/swift/Concurrency/Actor.h new file mode 100644 index 0000000000000..3bec6a2ab884a --- /dev/null +++ b/include/swift/Concurrency/Actor.h @@ -0,0 +1,71 @@ +//===--- Actor.h - Swift concurrency actor declarations ---------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Actor-related declarations that are also used by Remote Mirror. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_CONCURRENCY_ACTOR_H +#define SWIFT_CONCURRENCY_ACTOR_H + +#include + +namespace swift { +namespace concurrency { +namespace ActorFlagConstants { + +enum : uint32_t { + // Bits 0-2: Actor state + // + // Possible state transitions for an actor: + // + // Idle -> Running + // Running -> Idle + // Running -> Scheduled + // Scheduled -> Running + // Idle -> Deallocated + // Running -> Zombie_ReadyForDeallocation + // Zombie_ReadyForDeallocation -> Deallocated + // + // It is possible for an actor to be in Running and yet completely released + // by clients. However, the actor needs to be kept alive until it is done + // executing the task that is running on it and gives it up. It is only + // after that that we can safely deallocate it. + ActorStateMask = 0x7, + + /// The actor is not currently scheduled. Completely redundant + /// with the job list being empty. + Idle = 0x0, + /// There actor is scheduled + Scheduled = 0x1, + /// There is currently a thread running the actor. + Running = 0x2, + /// The actor is ready for deallocation once it stops running + Zombie_ReadyForDeallocation = 0x3, + + // Bit 3 + DistributedRemote = 0x8, + // Bit 4 + IsPriorityEscalated = 0x10, + + // Bits 8 - 15. We only need 8 bits of the whole size_t to represent Job + // Priority + PriorityMask = 0xFF00, + PriorityAndOverrideMask = PriorityMask | IsPriorityEscalated, + PriorityShift = 0x8, +}; + +} // namespace ActorFlagConstants +} // namespace concurrency +} // namespace swift + +#endif // SWIFT_CONCURRENCY_ACTOR_H diff --git a/include/swift/Reflection/ReflectionContext.h b/include/swift/Reflection/ReflectionContext.h index da0e4c866ace7..7ad641a95bd7c 100644 --- a/include/swift/Reflection/ReflectionContext.h +++ b/include/swift/Reflection/ReflectionContext.h @@ -27,6 +27,7 @@ #include "swift/ABI/Enum.h" #include "swift/ABI/ObjectFile.h" +#include "swift/Concurrency/Actor.h" #include "swift/Remote/MemoryReader.h" #include "swift/Remote/MetadataReader.h" #include "swift/Reflection/Records.h" @@ -58,8 +59,11 @@ // with escalation | present | full // with escalation | not present | DEGRADED // -// Currently, degraded info means that IsRunning is not available (indicated -// with `HasIsRunning = false`) and async backtraces are not provided. +// Currently, degraded info has these effects: +// 1. Task.IsRunning is not available, indicated with Task.HasIsRunning = false. +// 2. Task async backtraces are not provided. +// 3. Task and actor thread ports are not available, indicated with +// HasThreadPort = false. #if __has_include() #include @@ -185,6 +189,9 @@ class ReflectionContext bool IsRunning; bool IsEnqueued; + bool HasThreadPort; + uint32_t ThreadPort; + uint64_t Id; StoredPointer RunJob; StoredPointer AllocatorSlabPtr; @@ -193,8 +200,15 @@ class ReflectionContext }; struct ActorInfo { - StoredSize Flags; StoredPointer FirstJob; + + uint8_t State; + bool IsDistributedRemote; + bool IsPriorityEscalated; + uint8_t MaxPriority; + + bool HasThreadPort; + uint32_t ThreadPort; }; explicit ReflectionContext(std::shared_ptr reader) @@ -1532,6 +1546,44 @@ class ReflectionContext Task->PrivateStorage.Status.Flags[0] & ActiveTaskStatusFlags::IsRunning; } + std::pair getThreadPort( + const AsyncTask> *Task) { +#if HAS_DISPATCH_LOCK_IS_LOCKED + return {true, + dispatch_lock_owner(Task->PrivateStorage.Status.ExecutionLock[0])}; +#else + // The target runtime was built with priority escalation but we don't have + // the swift_concurrency_private.h header needed to decode the lock. + return {false, 0}; +#endif + } + + std::pair getThreadPort( + const AsyncTask> + *Task) { + // Tasks without escalation have no thread port to query. + return {false, 0}; + } + + std::pair getThreadPort( + const DefaultActorImpl> + *Actor) { +#if HAS_DISPATCH_LOCK_IS_LOCKED + return {true, dispatch_lock_owner(Actor->Status.DrainLock[0])}; +#else + // The target runtime was built with priority escalation but we don't have + // the swift_concurrency_private.h header needed to decode the lock. + return {false, 0}; +#endif + } + + std::pair + getThreadPort(const DefaultActorImpl< + Runtime, ActiveActorStatusWithoutEscalation> *Actor) { + // Actors without escalation have no thread port to query. + return {false, 0}; + } + template std::pair, AsyncTaskInfo> asyncTaskInfo(StoredPointer AsyncTaskPtr) { @@ -1557,6 +1609,8 @@ class ReflectionContext Info.IsEnqueued = TaskStatusFlags & ActiveTaskStatusFlags::IsEnqueued; setIsRunning(Info, AsyncTaskObj.get()); + std::tie(Info.HasThreadPort, Info.ThreadPort) = + getThreadPort(AsyncTaskObj.get()); Info.Id = AsyncTaskObj->Id | ((uint64_t)AsyncTaskObj->PrivateStorage.Id << 32); @@ -1626,15 +1680,26 @@ class ReflectionContext return {std::string("failure reading actor"), {}}; ActorInfo Info{}; - Info.Flags = ActorObj->Status.Flags[0]; - // Status is the low 3 bits of Flags. Status of 0 is Idle. Don't read - // FirstJob when idle. - auto Status = Info.Flags & 0x7; - if (Status != 0) { + uint32_t Flags = ActorObj->Status.Flags[0]; + Info.State = Flags & concurrency::ActorFlagConstants::ActorStateMask; + Info.IsDistributedRemote = + Flags & concurrency::ActorFlagConstants::DistributedRemote; + Info.IsPriorityEscalated = + Flags & concurrency::ActorFlagConstants::IsPriorityEscalated; + Info.MaxPriority = + (Flags & concurrency::ActorFlagConstants::PriorityMask) >> + concurrency::ActorFlagConstants::PriorityShift; + + // Don't read FirstJob when idle. + if (Info.State != concurrency::ActorFlagConstants::Idle) { // This is a JobRef which stores flags in the low bits. Info.FirstJob = ActorObj->Status.FirstJob & ~StoredPointer(0x3); } + + std::tie(Info.HasThreadPort, Info.ThreadPort) = + getThreadPort(ActorObj.get()); + return {llvm::None, Info}; } diff --git a/include/swift/Reflection/RuntimeInternals.h b/include/swift/Reflection/RuntimeInternals.h index 6941f1546750c..c4e20b7840679 100644 --- a/include/swift/Reflection/RuntimeInternals.h +++ b/include/swift/Reflection/RuntimeInternals.h @@ -174,6 +174,7 @@ struct ActiveActorStatusWithoutEscalation { template struct DefaultActorImpl { HeapObject HeapObject; + Job JobStorage; ActiveActorStatus Status; }; diff --git a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h index 04b3165a1bd2e..800b27ea5c5c5 100644 --- a/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h +++ b/include/swift/SwiftRemoteMirror/SwiftRemoteMirrorTypes.h @@ -18,6 +18,7 @@ #ifndef SWIFT_REMOTE_MIRROR_TYPES_H #define SWIFT_REMOTE_MIRROR_TYPES_H +#include #include #ifdef __cplusplus @@ -235,18 +236,21 @@ typedef struct swift_async_task_info { unsigned Kind; unsigned EnqueuePriority; - uint8_t IsChildTask; - uint8_t IsFuture; - uint8_t IsGroupChildTask; - uint8_t IsAsyncLetTask; + bool IsChildTask; + bool IsFuture; + bool IsGroupChildTask; + bool IsAsyncLetTask; unsigned MaxPriority; - uint8_t IsCancelled; - uint8_t IsStatusRecordLocked; - uint8_t IsEscalated; - uint8_t HasIsRunning; // If false, the IsRunning flag is not valid. - uint8_t IsRunning; - uint8_t IsEnqueued; + bool IsCancelled; + bool IsStatusRecordLocked; + bool IsEscalated; + bool HasIsRunning; // If false, the IsRunning flag is not valid. + bool IsRunning; + bool IsEnqueued; + + bool HasThreadPort; + uint32_t ThreadPort; uint64_t Id; swift_reflection_ptr_t RunJob; @@ -265,8 +269,15 @@ typedef struct swift_actor_info { /// swift_reflection call on the given context. const char *Error; - uint64_t Flags; + uint8_t State; + bool IsDistributedRemote; + bool IsPriorityEscalated; + uint8_t MaxPriority; + swift_reflection_ptr_t FirstJob; + + bool HasThreadPort; + uint32_t ThreadPort; } swift_actor_info_t; /// An opaque pointer to a context which maintains state and diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index 87031cb1800ed..b848ef2850edd 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -38,6 +38,7 @@ #include "swift/ABI/Task.h" #include "swift/ABI/Actor.h" #include "swift/Basic/ListMerger.h" +#include "swift/Concurrency/Actor.h" #ifdef SWIFT_CONCURRENCY_BACK_DEPLOYMENT // All platforms where we care about back deployment have a known // configurations. @@ -608,47 +609,6 @@ class JobRef { /// DefaultActorImpl. The additional alignment requirements are /// enforced by static asserts below. class alignas(sizeof(void *) * 2) ActiveActorStatus { - enum : uint32_t { - // Bits 0-2: Actor state - // - // Possible state transitions for an actor: - // - // Idle -> Running - // Running -> Idle - // Running -> Scheduled - // Scheduled -> Running - // Idle -> Deallocated - // Running -> Zombie_ReadyForDeallocation - // Zombie_ReadyForDeallocation -> Deallocated - // - // It is possible for an actor to be in Running and yet completely released - // by clients. However, the actor needs to be kept alive until it is done - // executing the task that is running on it and gives it up. It is only - // after that that we can safely deallocate it. - ActorStateMask = 0x7, - - /// The actor is not currently scheduled. Completely redundant - /// with the job list being empty. - Idle = 0x0, - /// There actor is scheduled - Scheduled = 0x1, - /// There is currently a thread running the actor. - Running = 0x2, - /// The actor is ready for deallocation once it stops running - Zombie_ReadyForDeallocation = 0x3, - - // Bit 3 - DistributedRemote = 0x8, - // Bit 4 - isPriorityEscalated = 0x10, - - // Bits 8 - 15. We only need 8 bits of the whole size_t to represent Job - // Priority - PriorityMask = 0xFF00, - PriorityAndOverrideMask = PriorityMask | isPriorityEscalated, - PriorityShift = 0x8, - }; - #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES uint32_t Flags; dispatch_lock_t DrainLock; @@ -673,10 +633,10 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus { #endif uint32_t getActorState() const { - return Flags & ActorStateMask; + return Flags & concurrency::ActorFlagConstants::ActorStateMask; } uint32_t setActorState(uint32_t state) const { - return (Flags & ~ActorStateMask) | state; + return (Flags & ~concurrency::ActorFlagConstants::ActorStateMask) | state; } public: @@ -689,17 +649,22 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus { : Flags(), FirstJob(JobRef()) {} #endif - bool isDistributedRemote() const { return Flags & DistributedRemote; } + bool isDistributedRemote() const { + return Flags & concurrency::ActorFlagConstants::DistributedRemote; + } ActiveActorStatus withDistributedRemote() const { #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION - return ActiveActorStatus(Flags | DistributedRemote, DrainLock, FirstJob); + return ActiveActorStatus( + Flags | concurrency::ActorFlagConstants::DistributedRemote, DrainLock, + FirstJob); #else - return ActiveActorStatus(Flags | DistributedRemote, FirstJob); + return ActiveActorStatus( + Flags | concurrency::ActorFlagConstants::DistributedRemote, FirstJob); #endif } bool isIdle() const { - bool isIdle = (getActorState() == Idle); + bool isIdle = (getActorState() == concurrency::ActorFlagConstants::Idle); if (isIdle) { assert(!FirstJob); } @@ -707,57 +672,79 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus { } ActiveActorStatus withIdle() const { #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION - return ActiveActorStatus(setActorState(Idle), DLOCK_OWNER_NULL, FirstJob); + return ActiveActorStatus( + setActorState(concurrency::ActorFlagConstants::Idle), DLOCK_OWNER_NULL, + FirstJob); #else - return ActiveActorStatus(setActorState(Idle), FirstJob); + return ActiveActorStatus( + setActorState(concurrency::ActorFlagConstants::Idle), FirstJob); #endif } bool isAnyRunning() const { uint32_t state = getActorState(); - return (state == Running) || (state == Zombie_ReadyForDeallocation); + return (state == concurrency::ActorFlagConstants::Running) || + (state == + concurrency::ActorFlagConstants::Zombie_ReadyForDeallocation); } bool isRunning() const { - return getActorState() == Running; + return getActorState() == concurrency::ActorFlagConstants::Running; } ActiveActorStatus withRunning() const { #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION - return ActiveActorStatus(setActorState(Running), dispatch_lock_value_for_self(), FirstJob); + return ActiveActorStatus( + setActorState(concurrency::ActorFlagConstants::Running), + dispatch_lock_value_for_self(), FirstJob); #else - return ActiveActorStatus(setActorState(Running), FirstJob); + return ActiveActorStatus( + setActorState(concurrency::ActorFlagConstants::Running), FirstJob); #endif } bool isScheduled() const { - return getActorState() == Scheduled; + return getActorState() == concurrency::ActorFlagConstants::Scheduled; } ActiveActorStatus withScheduled() const { #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION - return ActiveActorStatus(setActorState(Scheduled), DLOCK_OWNER_NULL, FirstJob); + return ActiveActorStatus( + setActorState(concurrency::ActorFlagConstants::Scheduled), + DLOCK_OWNER_NULL, FirstJob); #else - return ActiveActorStatus(setActorState(Scheduled), FirstJob); + return ActiveActorStatus( + setActorState(concurrency::ActorFlagConstants::Scheduled), FirstJob); #endif } bool isZombie_ReadyForDeallocation() const { - return getActorState() == Zombie_ReadyForDeallocation; + return getActorState() == + concurrency::ActorFlagConstants::Zombie_ReadyForDeallocation; } ActiveActorStatus withZombie_ReadyForDeallocation() const { #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION assert(dispatch_lock_owner(DrainLock) != DLOCK_OWNER_NULL); - return ActiveActorStatus(setActorState(Zombie_ReadyForDeallocation), DrainLock, FirstJob); + return ActiveActorStatus( + setActorState( + concurrency::ActorFlagConstants::Zombie_ReadyForDeallocation), + DrainLock, FirstJob); #else - return ActiveActorStatus(setActorState(Zombie_ReadyForDeallocation), FirstJob); + return ActiveActorStatus( + setActorState( + concurrency::ActorFlagConstants::Zombie_ReadyForDeallocation), + FirstJob); #endif } JobPriority getMaxPriority() const { - return (JobPriority) ((Flags & PriorityMask) >> PriorityShift); + return ( + JobPriority)((Flags & concurrency::ActorFlagConstants::PriorityMask) >> + concurrency::ActorFlagConstants::PriorityShift); } ActiveActorStatus withNewPriority(JobPriority priority) const { - uint32_t flags = Flags & ~PriorityAndOverrideMask; - flags |= (uint32_t(priority) << PriorityShift); + uint32_t flags = + Flags & ~concurrency::ActorFlagConstants::PriorityAndOverrideMask; + flags |= + (uint32_t(priority) << concurrency::ActorFlagConstants::PriorityShift); #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION return ActiveActorStatus(flags, DrainLock, FirstJob); #else @@ -768,13 +755,19 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus { return withNewPriority(JobPriority::Unspecified); } - bool isMaxPriorityEscalated() const { return Flags & isPriorityEscalated; } + bool isMaxPriorityEscalated() const { + return Flags & concurrency::ActorFlagConstants::IsPriorityEscalated; + } ActiveActorStatus withEscalatedPriority(JobPriority priority) const { - JobPriority currentPriority = JobPriority((Flags & PriorityMask) >> PriorityShift); + JobPriority currentPriority = + JobPriority((Flags & concurrency::ActorFlagConstants::PriorityMask) >> + concurrency::ActorFlagConstants::PriorityShift); assert(priority > currentPriority); - uint32_t flags = (Flags & ~PriorityMask) | (uint32_t(priority) << PriorityShift); - flags |= isPriorityEscalated; + uint32_t flags = + (Flags & ~concurrency::ActorFlagConstants::PriorityMask) | + (uint32_t(priority) << concurrency::ActorFlagConstants::PriorityShift); + flags |= concurrency::ActorFlagConstants::IsPriorityEscalated; #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION return ActiveActorStatus(flags, DrainLock, FirstJob); #else @@ -783,9 +776,13 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus { } ActiveActorStatus withoutEscalatedPriority() const { #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION - return ActiveActorStatus(Flags & ~isPriorityEscalated, DrainLock, FirstJob); + return ActiveActorStatus( + Flags & ~concurrency::ActorFlagConstants::IsPriorityEscalated, + DrainLock, FirstJob); #else - return ActiveActorStatus(Flags & ~isPriorityEscalated, FirstJob); + return ActiveActorStatus( + Flags & ~concurrency::ActorFlagConstants::IsPriorityEscalated, + FirstJob); #endif } @@ -823,16 +820,16 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus { // the enum values, but this explicit conversion provides room for change. uint8_t traceState = 255; switch (getActorState()) { - case ActiveActorStatus::Idle: + case concurrency::ActorFlagConstants::Idle: traceState = 0; break; - case ActiveActorStatus::Scheduled: + case concurrency::ActorFlagConstants::Scheduled: traceState = 1; break; - case ActiveActorStatus::Running: + case concurrency::ActorFlagConstants::Running: traceState = 2; break; - case ActiveActorStatus::Zombie_ReadyForDeallocation: + case concurrency::ActorFlagConstants::Zombie_ReadyForDeallocation: traceState = 3; break; } diff --git a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp index 7a0d548819076..53b21e7d3c943 100644 --- a/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp +++ b/stdlib/public/SwiftRemoteMirror/SwiftRemoteMirror.cpp @@ -892,6 +892,9 @@ swift_reflection_asyncTaskInfo(SwiftReflectionContextRef ContextRef, Result.IsEnqueued = TaskInfo.IsEnqueued; Result.Id = TaskInfo.Id; + Result.HasThreadPort = TaskInfo.HasThreadPort; + Result.ThreadPort = TaskInfo.ThreadPort; + Result.RunJob = TaskInfo.RunJob; Result.AllocatorSlabPtr = TaskInfo.AllocatorSlabPtr; @@ -919,13 +922,19 @@ swift_reflection_actorInfo(SwiftReflectionContextRef ContextRef, swift_reflection_ptr_t ActorPtr) { auto Context = ContextRef->nativeContext; llvm::Optional Error; - NativeReflectionContext::ActorInfo TaskInfo; - std::tie(Error, TaskInfo) = Context->actorInfo(ActorPtr); + NativeReflectionContext::ActorInfo ActorInfo; + std::tie(Error, ActorInfo) = Context->actorInfo(ActorPtr); swift_actor_info_t Result = {}; Result.Error = returnableCString(ContextRef, Error); - Result.Flags = TaskInfo.Flags; - Result.FirstJob = TaskInfo.FirstJob; + Result.State = ActorInfo.State; + Result.IsDistributedRemote = ActorInfo.IsDistributedRemote; + Result.IsPriorityEscalated = ActorInfo.IsPriorityEscalated; + Result.MaxPriority = ActorInfo.MaxPriority; + Result.FirstJob = ActorInfo.FirstJob; + + Result.HasThreadPort = ActorInfo.HasThreadPort; + Result.ThreadPort = ActorInfo.ThreadPort; return Result; } diff --git a/tools/swift-inspect/Sources/swift-inspect/DarwinRemoteProcess.swift b/tools/swift-inspect/Sources/swift-inspect/DarwinRemoteProcess.swift index 27aebe0d0023d..7993443ed667a 100644 --- a/tools/swift-inspect/Sources/swift-inspect/DarwinRemoteProcess.swift +++ b/tools/swift-inspect/Sources/swift-inspect/DarwinRemoteProcess.swift @@ -28,6 +28,8 @@ internal final class DarwinRemoteProcess: RemoteProcess { private var swiftCore: CSTypeRef private let swiftConcurrency: CSTypeRef + private lazy var threadInfos = getThreadInfos() + static var QueryDataLayout: QueryDataLayoutFunction { return { (context, type, _, output) in guard let output = output else { return 0 } @@ -178,59 +180,115 @@ internal final class DarwinRemoteProcess: RemoteProcess { } extension DarwinRemoteProcess { - internal var currentTasks: [(threadID: UInt64, currentTask: swift_addr_t)] { - var threadList: UnsafeMutablePointer? - var threadCount: mach_msg_type_number_t = 0 + private class PortList: Sequence { + let buffer: UnsafeBufferPointer - let result = task_threads(self.task, &threadList, &threadCount) - guard result == KERN_SUCCESS else { - print("unable to gather threads for process: \(String(cString: mach_error_string(result))) (0x\(String(result, radix: 16)))") - return [] + init?(task: task_t) { + var threadList: UnsafeMutablePointer? + var threadCount: mach_msg_type_number_t = 0 + + let result = task_threads(task, &threadList, &threadCount) + guard result == KERN_SUCCESS else { + print("unable to gather threads for process: \(String(cString: mach_error_string(result))) (0x\(String(result, radix: 16)))") + return nil + } + + buffer = UnsafeBufferPointer(start: threadList, count: Int(threadCount)) } - defer { + deinit { // Deallocate the port rights for the threads. - for i in 0 ..< Int(threadCount) { - mach_port_deallocate(mach_task_self_, threadList![i]) + for thread in self { + mach_port_deallocate(mach_task_self_, thread) } // Deallocate the thread list. - let pointer = vm_address_t(truncatingIfNeeded: Int(bitPattern: threadList)) - let size = vm_size_t(MemoryLayout.size) * vm_size_t(threadCount) + let pointer = vm_address_t(truncatingIfNeeded: Int(bitPattern: buffer.baseAddress)) + let size = vm_size_t(MemoryLayout.size) * vm_size_t(buffer.count) vm_deallocate(mach_task_self_, pointer, size) } - var results: [(threadID: UInt64, currentTask: swift_addr_t)] = [] - for i in 0 ..< Int(threadCount) { - let THREAD_IDENTIFIER_INFO_COUNT = - MemoryLayout.size / MemoryLayout.size - var info = thread_identifier_info_data_t() - var infoCount = mach_msg_type_number_t(THREAD_IDENTIFIER_INFO_COUNT) - - withUnsafeMutablePointer(to: &info) { - $0.withMemoryRebound(to: integer_t.self, capacity: THREAD_IDENTIFIER_INFO_COUNT) { - let result = - thread_info(threadList![i], thread_flavor_t(THREAD_IDENTIFIER_INFO), - $0, &infoCount) - guard result == KERN_SUCCESS else { - print("unable to get info for thread \(i): \(String(cString: mach_error_string(result))) (0x\(String(result, radix: 16)))") - return - } - } + func makeIterator() -> UnsafeBufferPointer.Iterator { + return buffer.makeIterator() + } + } + + private struct ThreadInfo { + var threadID: UInt64 + var tlsStart: UInt64 + var kernelObject: UInt32? + } + + private func getThreadInfos() -> [ThreadInfo] { + guard let threads = PortList(task: self.task) else { + return [] + } + return threads.compactMap { + guard let info = getThreadInfo(thread: $0) else { + return nil } + guard let kernelObj = getKernelObject(task: mach_task_self_, port: $0) else { + return nil + } + return ThreadInfo(threadID: info.thread_id, + tlsStart: info.thread_handle, + kernelObject: kernelObj) + } + } - let tlsStart = info.thread_handle - if tlsStart == 0 { continue } + private func getKernelObject(task: task_t, port: mach_port_t) -> UInt32? { + var object: UInt32 = 0 + var type: UInt32 = 0 + let result = mach_port_kernel_object(task, port, &type, &object) + guard result == KERN_SUCCESS else { + return nil + } + return object + } + + private func getThreadInfo(thread: thread_t) -> thread_identifier_info_data_t? { + let THREAD_IDENTIFIER_INFO_COUNT = + MemoryLayout.size / MemoryLayout.size + var info = thread_identifier_info_data_t() + var infoCount = mach_msg_type_number_t(THREAD_IDENTIFIER_INFO_COUNT) + var result: kern_return_t = 0 + + withUnsafeMutablePointer(to: &info) { + $0.withMemoryRebound(to: integer_t.self, capacity: THREAD_IDENTIFIER_INFO_COUNT) { + result = thread_info(thread, thread_flavor_t(THREAD_IDENTIFIER_INFO), + $0, &infoCount) + } + } + guard result == KERN_SUCCESS else { + print("unable to get info for thread port \(thread): \(String(cString: mach_error_string(result))) (0x\(String(result, radix: 16)))") + return nil + } + return info + } +} + +extension DarwinRemoteProcess { + internal var currentTasks: [(threadID: UInt64, currentTask: swift_addr_t)] { + return threadInfos.compactMap { + let tlsStart = $0.tlsStart + if tlsStart == 0 { return nil } let SWIFT_CONCURRENCY_TASK_KEY = 103 let currentTaskPointer = tlsStart + UInt64(SWIFT_CONCURRENCY_TASK_KEY * MemoryLayout.size) - if let pointer = read(address: currentTaskPointer, size: MemoryLayout.size) { - let currentTask = pointer.load(as: UInt.self) - results.append((threadID: info.thread_id, currentTask: swift_addr_t(currentTask))) + guard let pointer = read(address: currentTaskPointer, size: MemoryLayout.size) else { + return nil } + let currentTask = pointer.load(as: UInt.self) + return (threadID: $0.threadID, currentTask: swift_addr_t(currentTask)) + } + } + + internal func getThreadID(remotePort: thread_t) -> UInt64? { + guard let remoteThreadObj = getKernelObject(task: self.task, port: remotePort) else { + return nil } - return results + return threadInfos.first{ $0.kernelObject == remoteThreadObj }?.threadID } } diff --git a/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift b/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift index af1aeb0897cf3..75b4363f58033 100644 --- a/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift +++ b/tools/swift-inspect/Sources/swift-inspect/Operations/DumpConcurrency.swift @@ -54,6 +54,7 @@ fileprivate class ConcurrencyDumper { var hasIsRunning: Bool var isRunning: Bool var isEnqueued: Bool + var threadPort: UInt32? var id: UInt64 var runJob: swift_reflection_ptr_t var allocatorSlabPtr: swift_reflection_ptr_t @@ -100,7 +101,7 @@ fileprivate class ConcurrencyDumper { func gatherHeapInfo() -> HeapInfo { var result = HeapInfo() - + process.iterateHeap { (pointer, size) in let metadata = swift_reflection_ptr_t(swift_reflection_metadataForObject(context, UInt(pointer))) if metadata == jobMetadata { @@ -198,17 +199,20 @@ fileprivate class ConcurrencyDumper { address: task, kind: reflectionInfo.Kind, enqueuePriority: reflectionInfo.EnqueuePriority, - isChildTask: reflectionInfo.IsChildTask != 0, - isFuture: reflectionInfo.IsFuture != 0, - isGroupChildTask: reflectionInfo.IsGroupChildTask != 0, - isAsyncLetTask: reflectionInfo.IsAsyncLetTask != 0, + isChildTask: reflectionInfo.IsChildTask, + isFuture: reflectionInfo.IsFuture, + isGroupChildTask: reflectionInfo.IsGroupChildTask, + isAsyncLetTask: reflectionInfo.IsAsyncLetTask, maxPriority: reflectionInfo.MaxPriority, - isCancelled: reflectionInfo.IsCancelled != 0, - isStatusRecordLocked: reflectionInfo.IsStatusRecordLocked != 0, - isEscalated: reflectionInfo.IsEscalated != 0, - hasIsRunning: reflectionInfo.HasIsRunning != 0, - isRunning: reflectionInfo.IsRunning != 0, - isEnqueued: reflectionInfo.IsEnqueued != 0, + isCancelled: reflectionInfo.IsCancelled, + isStatusRecordLocked: reflectionInfo.IsStatusRecordLocked, + isEscalated: reflectionInfo.IsEscalated, + hasIsRunning: reflectionInfo.HasIsRunning, + isRunning: reflectionInfo.IsRunning, + isEnqueued: reflectionInfo.IsEnqueued, + threadPort: reflectionInfo.HasThreadPort + ? reflectionInfo.ThreadPort + : nil, id: reflectionInfo.Id, runJob: reflectionInfo.RunJob, allocatorSlabPtr: reflectionInfo.AllocatorSlabPtr, @@ -257,22 +261,6 @@ fileprivate class ConcurrencyDumper { return remove(from: name, upTo: " resume partial function for ") } - func flagsStrings(flags: T, strings: [T: String]) -> [String] { - return strings.sorted{ $0.key < $1.key } - .filter({ ($0.key & flags) != 0}) - .map{ $0.value } - } - - func flagsString(flags: T, strings: [T: String]) -> String { - let flagStrs = flagsStrings(flags: flags, strings: strings) - if flagStrs.isEmpty { - return "0" - } - - let flagsStr = flagStrs.joined(separator: "|") - return flagsStr - } - func decodeTaskFlags(_ info: TaskInfo) -> String { var flags: [String] = [] if info.isChildTask { flags.append("childTask") } @@ -289,29 +277,28 @@ fileprivate class ConcurrencyDumper { return flagsStr } - func decodeActorFlags(_ flags: UInt64) -> ( - status: String, + func decodeActorFlags(_ info: swift_actor_info_t) -> ( + state: String, flags: String, - maxPriority: UInt64 + maxPriority: UInt8 ) { - let statuses: [UInt64: String] = [ + let states: [UInt8: String] = [ 0: "idle", 1: "scheduled", 2: "running", 3: "zombie-latching", 4: "zombie-ready-for-deallocation" ] - let flagsString = flagsString(flags: flags, strings: [ - 1 << 3: "hasActiveInlineJob", - 1 << 4: "isDistributedRemote" - ]) - let status = flags & 0x7 - let maxPriority = (flags >> 8) & 0xff + var flags: [String] = [] + if info.IsPriorityEscalated { flags.append("priorityEscalated") } + if info.IsDistributedRemote { flags.append("distributedRemote") } + let flagsStr = flags.isEmpty ? "0" : flags.joined(separator: "|") + return ( - status: statuses[status] ?? "unknown(\(status))", - flags: flagsString, - maxPriority: maxPriority + state: states[info.State] ?? "unknown(\(info.State))", + flags: flagsStr, + maxPriority: info.MaxPriority ) } @@ -323,7 +310,7 @@ fileprivate class ConcurrencyDumper { print("warning: unable to decode is-running state of target tasks, running state and async backtraces will not be printed") } - let taskToThread: [swift_addr_t: swift_reflection_ptr_t] = + let taskToThread: [swift_addr_t: UInt64] = Dictionary(threadCurrentTasks.map{ ($1, $0) }, uniquingKeysWith: { $1 }) var lastChilds: [Bool] = [] @@ -364,12 +351,17 @@ fileprivate class ConcurrencyDumper { let flags = decodeTaskFlags(task) output("Task \(hex: task.id) - flags=\(flags) enqueuePriority=\(hex: task.enqueuePriority) maxPriority=\(hex: task.maxPriority) address=\(hex: task.address)") - if let thread = taskToThread[task.address] { + if let thread = taskToThread[swift_addr_t(task.address)] { output("current task on thread \(hex: thread)") } if let parent = task.parent { output("parent: \(hex: parent)") } + if let threadPort = task.threadPort, threadPort != 0 { + if let threadID = process.getThreadID(remotePort: threadPort) { + output("waiting on thread: port=\(hex: threadPort) id=\(hex: threadID)") + } + } if let first = symbolicatedBacktrace.first { output("async backtrace: \(first)") @@ -400,11 +392,22 @@ fileprivate class ConcurrencyDumper { for actor in actors { let metadata = swift_reflection_metadataForObject(context, UInt(actor)) let metadataName = name(metadata: swift_reflection_ptr_t(metadata)) ?? "" - let info = swift_reflection_actorInfo(context, actor); + let info = swift_reflection_actorInfo(context, actor) + if let error = info.Error { + print(String(utf8String: error) ?? "") + continue + } - let flags = decodeActorFlags(info.Flags) + let flags = decodeActorFlags(info) - print(" \(hex: actor) \(metadataName) status=\(flags.status) flags=\(flags.flags) maxPriority=\(hex: flags.maxPriority)") + print(" \(hex: actor) \(metadataName) state=\(flags.state) flags=\(flags.flags) maxPriority=\(hex: flags.maxPriority)") + if info.HasThreadPort && info.ThreadPort != 0 { + if let threadID = process.getThreadID(remotePort: info.ThreadPort) { + print(" waiting on thread: port=\(hex: info.ThreadPort) id=\(hex: threadID)") + } else { + print(" waiting on thread: port=\(hex: info.ThreadPort) (unknown thread ID)") + } + } func jobStr(_ job: swift_reflection_ptr_t) -> String { if let task = tasks[job] { @@ -425,7 +428,7 @@ fileprivate class ConcurrencyDumper { } } } - print("") + print("") } } From 7785bf438b652c430764ed2ba89cf84115f9e1a0 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 18 Mar 2022 15:30:25 -0700 Subject: [PATCH 221/242] Make sure functions defined in a header are inline. Fixes rdar://89976658. --- include/swift/Runtime/DispatchShims.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/swift/Runtime/DispatchShims.h b/include/swift/Runtime/DispatchShims.h index 810489c5c8901..eabc96310dfd8 100644 --- a/include/swift/Runtime/DispatchShims.h +++ b/include/swift/Runtime/DispatchShims.h @@ -20,7 +20,7 @@ // Provide wrappers with runtime checks to make sure that the dispatch functions // are only called on OS-es where they are supported -dispatch_thread_override_info_s +static inline dispatch_thread_override_info_s swift_dispatch_thread_get_current_override_qos_floor() { if (__builtin_available(macOS 9998, iOS 9998, tvOS 9998, watchOS 9998, *)) { @@ -30,7 +30,7 @@ swift_dispatch_thread_get_current_override_qos_floor() return (dispatch_thread_override_info_s) {0}; } -int +static inline int swift_dispatch_thread_override_self(qos_class_t override_qos) { if (__builtin_available(macOS 9998, iOS 9998, tvOS 9998, watchOS 9998, *)) { @@ -40,7 +40,7 @@ swift_dispatch_thread_override_self(qos_class_t override_qos) { return 0; } -int +static inline int swift_dispatch_lock_override_start_with_debounce(dispatch_lock_t *lock_addr, dispatch_tid_t expected_thread, qos_class_t override_to_apply) { @@ -51,7 +51,7 @@ swift_dispatch_lock_override_start_with_debounce(dispatch_lock_t *lock_addr, return 0; } -int +static inline int swift_dispatch_lock_override_end(qos_class_t override_to_end) { if (__builtin_available(macOS 9998, iOS 9998, tvOS 9998, watchOS 9998, *)) { return dispatch_lock_override_end(override_to_end); From bc686e733e97e290000b90b60d00d80c54089360 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Fri, 18 Mar 2022 18:02:18 -0700 Subject: [PATCH 222/242] [Diagnostics] Change phrasing of the existential_requires_any warning The previous warning "must be explicitly marked as 'any'" isn't clear if you don't already know the feature, this new phrasing should make it clearer by including the correct spelling of the type as an example. Fixes rdar://90384448 --- include/swift/AST/DiagnosticsSema.def | 4 +-- lib/Sema/TypeCheckType.cpp | 2 ++ test/Generics/function_defs.swift | 2 +- test/decl/nested/protocol.swift | 2 +- test/decl/protocol/conforms/inherited.swift | 8 +++--- ...ntial_member_accesses_self_assoctype.swift | 2 +- ...member_accesses_self_assoctype_fixit.swift | 16 +++++------ test/decl/protocol/protocols.swift | 2 +- .../decl/protocol/recursive_requirement.swift | 4 +-- test/type/explicit_existential.swift | 28 +++++++++---------- test/type/protocol_types.swift | 16 +++++------ 11 files changed, 44 insertions(+), 42 deletions(-) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 854ef71461e69..fabb2c22209ab 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4737,8 +4737,8 @@ ERROR(any_not_existential,none, "'any' has no effect on %select{concrete type|type parameter}0 %1", (bool, Type)) ERROR(existential_requires_any,none, - "%select{protocol |}1%0 as a type must be explicitly marked as 'any'", - (Type, bool)) + "use of %select{protocol |}2%0 as a type must be written %1", + (Type, Type, bool)) ERROR(nonisolated_let,none, "'nonisolated' is meaningless on 'let' declarations because " diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 81341c76a5d8a..af8f485339440 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -4254,6 +4254,7 @@ class ExistentialTypeVisitor Ctx.Diags.diagnose(comp->getNameLoc(), diag::existential_requires_any, proto->getDeclaredInterfaceType(), + proto->getExistentialType(), /*isAlias=*/false) .limitBehavior(DiagnosticBehavior::Warning) .fixItReplace(replaceRepr->getSourceRange(), fix); @@ -4273,6 +4274,7 @@ class ExistentialTypeVisitor Ctx.Diags.diagnose(comp->getNameLoc(), diag::existential_requires_any, alias->getDeclaredInterfaceType(), + ExistentialType::get(alias->getDeclaredInterfaceType()), /*isAlias=*/true) .limitBehavior(DiagnosticBehavior::Warning) .fixItReplace(replaceRepr->getSourceRange(), fix); diff --git a/test/Generics/function_defs.swift b/test/Generics/function_defs.swift index bdb3a5abbd068..2a5083ffc317b 100644 --- a/test/Generics/function_defs.swift +++ b/test/Generics/function_defs.swift @@ -34,7 +34,7 @@ func min(_ x: T, y: T) -> T { //===----------------------------------------------------------------------===// func existential(_ t1: T, t2: T, u: U) { - var eqComp : EqualComparable = t1 // expected-warning {{protocol 'EqualComparable' as a type must be explicitly marked as 'any'}} + var eqComp : EqualComparable = t1 // expected-warning {{use of protocol 'EqualComparable' as a type must be written 'any EqualComparable'}} eqComp = u if t1.isEqual(eqComp) {} // expected-error{{cannot convert value of type 'any EqualComparable' to expected argument type 'T'}} if eqComp.isEqual(t2) {} diff --git a/test/decl/nested/protocol.swift b/test/decl/nested/protocol.swift index 2bda50bf6ee46..1c2a637fd4b73 100644 --- a/test/decl/nested/protocol.swift +++ b/test/decl/nested/protocol.swift @@ -31,7 +31,7 @@ protocol OuterProtocol { struct ConformsToOuterProtocol : OuterProtocol { typealias Hen = Int - func f() { let _ = InnerProtocol.self } // expected-warning {{protocol 'InnerProtocol' as a type must be explicitly marked as 'any'}} + func f() { let _ = InnerProtocol.self } // expected-warning {{use of protocol 'InnerProtocol' as a type must be written 'any InnerProtocol'}} } protocol Racoon { diff --git a/test/decl/protocol/conforms/inherited.swift b/test/decl/protocol/conforms/inherited.swift index 8b54508922117..12c54cc51fae6 100644 --- a/test/decl/protocol/conforms/inherited.swift +++ b/test/decl/protocol/conforms/inherited.swift @@ -167,13 +167,13 @@ class B : A { } func testB(_ b: B) { - var _: P1 = b // expected-warning {{protocol 'P1' as a type must be explicitly marked as 'any'}} - var _: P4 = b // expected-warning {{protocol 'P4' as a type must be explicitly marked as 'any'}} + var _: P1 = b // expected-warning {{use of protocol 'P1' as a type must be written 'any P1'}} + var _: P4 = b // expected-warning {{use of protocol 'P4' as a type must be written 'any P4'}} var _: P5 = b var _: P6 = b - var _: P7 = b // expected-warning {{protocol 'P7' as a type must be explicitly marked as 'any'}} + var _: P7 = b // expected-warning {{use of protocol 'P7' as a type must be written 'any P7'}} var _: P8 = b - var _: P9 = b // expected-warning {{protocol 'P9' as a type must be explicitly marked as 'any'}} + var _: P9 = b // expected-warning {{use of protocol 'P9' as a type must be written 'any P9'}} } // Class A5 conforms to P5 in an inheritable manner. diff --git a/test/decl/protocol/existential_member_accesses_self_assoctype.swift b/test/decl/protocol/existential_member_accesses_self_assoctype.swift index 97b6126ee0c4a..ed1fd9d144085 100644 --- a/test/decl/protocol/existential_member_accesses_self_assoctype.swift +++ b/test/decl/protocol/existential_member_accesses_self_assoctype.swift @@ -805,7 +805,7 @@ do { let _: any Class.Inner> & ConcreteAssocTypes = arg[ // FIXME: Sema thinks (any ConcreteAssocTypes).self is a function ref. - // expected-warning@+1 {{protocol 'ConcreteAssocTypes' as a type must be explicitly marked as 'any'}} + // expected-warning@+1 {{use of protocol 'ConcreteAssocTypes' as a type must be written 'any ConcreteAssocTypes'}} subscript4: Struct(), ConcreteAssocTypes.self, { true } ] } diff --git a/test/decl/protocol/existential_member_accesses_self_assoctype_fixit.swift b/test/decl/protocol/existential_member_accesses_self_assoctype_fixit.swift index 03b329bd6aecf..cae8780f031f9 100644 --- a/test/decl/protocol/existential_member_accesses_self_assoctype_fixit.swift +++ b/test/decl/protocol/existential_member_accesses_self_assoctype_fixit.swift @@ -12,12 +12,12 @@ protocol P { protocol Q {} do { - func test(p: P) { // expected-warning {{protocol 'P' as a type must be explicitly marked as 'any'}} + func test(p: P) { // expected-warning {{use of protocol 'P' as a type must be written 'any P'}} p.method(false) // expected-error {{member 'method' cannot be used on value of type 'any P'; consider using a generic constraint instead}} {{-1:16--1:17=<#generic parameter name#>}} {{-1:12--1:12=<<#generic parameter name#>: P>}} {{none}} } } do { - func test(p: ((P))) { // expected-warning {{protocol 'P' as a type must be explicitly marked as 'any'}} + func test(p: ((P))) { // expected-warning {{use of protocol 'P' as a type must be written 'any P'}} p.method(false) // expected-error {{member 'method' cannot be used on value of type 'any P'; consider using a generic constraint instead}} {{-1:18--1:19=<#generic parameter name#>}} {{-1:12--1:12=<<#generic parameter name#>: P>}} {{none}} } } @@ -57,12 +57,12 @@ do { } } do { - func test(p: P.Type) { // expected-warning {{protocol 'P' as a type must be explicitly marked as 'any'}} + func test(p: P.Type) { // expected-warning {{use of protocol 'P' as a type must be written 'any P'}} p.staticMethod(false) // expected-error {{member 'staticMethod' cannot be used on value of type 'any P.Type'; consider using a generic constraint instead}} {{-1:16--1:17=<#generic parameter name#>}} {{-1:12--1:12=<<#generic parameter name#>: P>}} {{none}} } } do { - func test(p: (P).Type) { // expected-warning {{protocol 'P' as a type must be explicitly marked as 'any'}} + func test(p: (P).Type) { // expected-warning {{use of protocol 'P' as a type must be written 'any P'}} p.staticMethod(false) // expected-error {{member 'staticMethod' cannot be used on value of type 'any (P).Type'; consider using a generic constraint instead}} {{-1:17--1:18=<#generic parameter name#>}} {{-1:12--1:12=<<#generic parameter name#>: P>}} {{none}} } } @@ -78,12 +78,12 @@ do { } do { - func test(p: P & Q) { // expected-warning {{protocol 'P' as a type must be explicitly marked as 'any'}} + func test(p: P & Q) { // expected-warning {{use of protocol 'P' as a type must be written 'any P'}} p.method(false) // expected-error {{member 'method' cannot be used on value of type 'any P & Q'; consider using a generic constraint instead}} {{-1:16--1:21=<#generic parameter name#>}} {{-1:12--1:12=<<#generic parameter name#>: P & Q>}} {{none}} } } do { - func test(p: ((P & Q))) { // expected-warning {{protocol 'P' as a type must be explicitly marked as 'any'}} + func test(p: ((P & Q))) { // expected-warning {{use of protocol 'P' as a type must be written 'any P'}} p.method(false) // expected-error {{member 'method' cannot be used on value of type 'any P & Q'; consider using a generic constraint instead}} {{-1:18--1:23=<#generic parameter name#>}} {{-1:12--1:12=<<#generic parameter name#>: P & Q>}} {{none}} } } @@ -123,12 +123,12 @@ do { } } do { - func test(p: (P & Q).Type) { // expected-warning {{protocol 'P' as a type must be explicitly marked as 'any'}} + func test(p: (P & Q).Type) { // expected-warning {{use of protocol 'P' as a type must be written 'any P'}} p.staticMethod(false) // expected-error {{member 'staticMethod' cannot be used on value of type 'any (P & Q).Type'; consider using a generic constraint instead}} {{-1:16--1:23=<#generic parameter name#>}} {{-1:12--1:12=<<#generic parameter name#>: P & Q>}} {{none}} } } do { - func test(p: ((P & Q)).Type) { // expected-warning {{protocol 'P' as a type must be explicitly marked as 'any'}} + func test(p: ((P & Q)).Type) { // expected-warning {{use of protocol 'P' as a type must be written 'any P'}} p.staticMethod(false) // expected-error {{member 'staticMethod' cannot be used on value of type 'any ((P & Q)).Type'; consider using a generic constraint instead}} {{-1:18--1:23=<#generic parameter name#>}} {{-1:12--1:12=<<#generic parameter name#>: P & Q>}} {{none}} } } diff --git a/test/decl/protocol/protocols.swift b/test/decl/protocol/protocols.swift index bc38ea3c70dd6..fb8a9c43b5148 100644 --- a/test/decl/protocol/protocols.swift +++ b/test/decl/protocol/protocols.swift @@ -446,7 +446,7 @@ func i(_ x : T?) -> Bool { // expected-warning@-1 {{checking a value with optional type 'T?' against type 'any P1' succeeds whenever the value is non-nil; did you mean to use '!= nil'?}} } func j(_ x : C1) -> Bool { - return x is P1 // expected-warning {{protocol 'P1' as a type must be explicitly marked as 'any'}} + return x is P1 // expected-warning {{use of protocol 'P1' as a type must be written 'any P1'}} } func k(_ x : C1?) -> Bool { return x is any P1 diff --git a/test/decl/protocol/recursive_requirement.swift b/test/decl/protocol/recursive_requirement.swift index eab9ac2f8687e..48a70a749dfe7 100644 --- a/test/decl/protocol/recursive_requirement.swift +++ b/test/decl/protocol/recursive_requirement.swift @@ -91,7 +91,7 @@ protocol AsExistentialB { } protocol AsExistentialAssocTypeA { - var delegate : AsExistentialAssocTypeB? { get } // expected-warning {{protocol 'AsExistentialAssocTypeB' as a type must be explicitly marked as 'any'}} + var delegate : AsExistentialAssocTypeB? { get } // expected-warning {{use of protocol 'AsExistentialAssocTypeB' as a type must be written 'any AsExistentialAssocTypeB'}} } protocol AsExistentialAssocTypeB { func aMethod(_ object : AsExistentialAssocTypeA) @@ -103,7 +103,7 @@ protocol AsExistentialAssocTypeAgainA { associatedtype Bar } protocol AsExistentialAssocTypeAgainB { - func aMethod(_ object : AsExistentialAssocTypeAgainA) // expected-warning {{protocol 'AsExistentialAssocTypeAgainA' as a type must be explicitly marked as 'any'}} + func aMethod(_ object : AsExistentialAssocTypeAgainA) // expected-warning {{use of protocol 'AsExistentialAssocTypeAgainA' as a type must be written 'any AsExistentialAssocTypeAgainA'}} } // SR-547 diff --git a/test/type/explicit_existential.swift b/test/type/explicit_existential.swift index 24bec0631ce28..345364fa67938 100644 --- a/test/type/explicit_existential.swift +++ b/test/type/explicit_existential.swift @@ -244,8 +244,8 @@ protocol Output { associatedtype A } -// expected-warning@+2{{protocol 'Input' as a type must be explicitly marked as 'any'}}{{30-35=any Input}} -// expected-warning@+1{{protocol 'Output' as a type must be explicitly marked as 'any'}}{{40-46=any Output}} +// expected-warning@+2{{use of protocol 'Input' as a type must be written 'any Input'}}{{30-35=any Input}} +// expected-warning@+1{{use of protocol 'Output' as a type must be written 'any Output'}}{{40-46=any Output}} typealias InvalidFunction = (Input) -> Output func testInvalidFunctionAlias(fn: InvalidFunction) {} @@ -253,7 +253,7 @@ typealias ExistentialFunction = (any Input) -> any Output func testFunctionAlias(fn: ExistentialFunction) {} typealias Constraint = Input -func testConstraintAlias(x: Constraint) {} // expected-warning{{'Constraint' (aka 'Input') as a type must be explicitly marked as 'any'}}{{29-39=any Constraint}} +func testConstraintAlias(x: Constraint) {} // expected-warning{{use of 'Constraint' (aka 'Input') as a type must be written 'any Constraint'}}{{29-39=any Constraint}} typealias Existential = any Input func testExistentialAlias(x: Existential, y: any Constraint) {} @@ -287,25 +287,25 @@ func testAnyFixIt() { func method() -> any HasAssoc {} } - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-18=any HasAssoc}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=any HasAssoc}} let _: HasAssoc = ConformingType() - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{19-27=any HasAssoc}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{19-27=any HasAssoc}} let _: Optional = nil - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-23=any HasAssoc.Type}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-23=any HasAssoc.Type}} let _: HasAssoc.Type = ConformingType.self - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-25=any (HasAssoc).Type}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-25=any (HasAssoc).Type}} let _: (HasAssoc).Type = ConformingType.self - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-27=any ((HasAssoc)).Type}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-27=any ((HasAssoc)).Type}} let _: ((HasAssoc)).Type = ConformingType.self - // expected-warning@+2 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-18=(any HasAssoc)}} - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{30-38=(any HasAssoc)}} + // expected-warning@+2 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=(any HasAssoc)}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{30-38=(any HasAssoc)}} let _: HasAssoc.Protocol = HasAssoc.self - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{11-19=any HasAssoc}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{11-19=any HasAssoc}} let _: (HasAssoc).Protocol = (any HasAssoc).self - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-18=(any HasAssoc)}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=(any HasAssoc)}} let _: HasAssoc? = ConformingType() - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-23=(any HasAssoc.Type)}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-23=(any HasAssoc.Type)}} let _: HasAssoc.Type? = ConformingType.self - // expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-18=(any HasAssoc)}} + // expected-warning@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=(any HasAssoc)}} let _: HasAssoc.Protocol? = (any HasAssoc).self } diff --git a/test/type/protocol_types.swift b/test/type/protocol_types.swift index 41df887c396d0..ca310db62f7e1 100644 --- a/test/type/protocol_types.swift +++ b/test/type/protocol_types.swift @@ -3,7 +3,7 @@ protocol HasSelfRequirements { func foo(_ x: Self) - func returnsOwnProtocol() -> HasSelfRequirements // expected-warning {{protocol 'HasSelfRequirements' as a type must be explicitly marked as 'any'}} + func returnsOwnProtocol() -> HasSelfRequirements // expected-warning {{use of protocol 'HasSelfRequirements' as a type must be written 'any HasSelfRequirements'}} } protocol Bar { // init() methods should not prevent use as an existential. @@ -74,7 +74,7 @@ do { func checkIt(_ js: Any) throws { switch js { - case let dbl as HasAssoc: // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} + case let dbl as HasAssoc: // expected-warning {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}} throw MyError.bad(dbl) default: @@ -83,7 +83,7 @@ do { } } -func testHasAssoc(_ x: Any, _: HasAssoc) { // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} +func testHasAssoc(_ x: Any, _: HasAssoc) { // expected-warning {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}} if let p = x as? any HasAssoc { p.foo() // don't crash here. } @@ -92,12 +92,12 @@ func testHasAssoc(_ x: Any, _: HasAssoc) { // expected-warning {{protocol 'HasAs typealias Assoc = Int func foo() {} - func method() -> HasAssoc {} // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} + func method() -> HasAssoc {} // expected-warning {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}} } } // SR-38 -var b: HasAssoc // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} +var b: HasAssoc // expected-warning {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}} // Further generic constraint error testing - typealias used inside statements protocol P {} @@ -118,20 +118,20 @@ typealias X = Struct1 _ = Struct1.self typealias AliasWhere = T -where T : HasAssoc, T.Assoc == HasAssoc // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} +where T : HasAssoc, T.Assoc == HasAssoc // expected-warning {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}} struct StructWhere where T : HasAssoc, T.Assoc == any HasAssoc {} -protocol ProtocolWhere where T == HasAssoc { // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} +protocol ProtocolWhere where T == HasAssoc { // expected-warning {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}} associatedtype T associatedtype U : HasAssoc where U.Assoc == any HasAssoc } -extension HasAssoc where Assoc == HasAssoc {} // expected-warning {{protocol 'HasAssoc' as a type must be explicitly marked as 'any'}} +extension HasAssoc where Assoc == HasAssoc {} // expected-warning {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}} func FunctionWhere(_: T) where T : HasAssoc, From 9b1fcedf2ffb1ccb1458761d0e6fcffca92af8f7 Mon Sep 17 00:00:00 2001 From: Kavon Farvardin Date: Fri, 18 Mar 2022 18:10:45 -0700 Subject: [PATCH 223/242] backtrack on part of SE-327 dealing with default-value exprs This effectively reverts https://github.com/apple/swift/commit/68237447794d7a9aeeeed25422076387b84b3c4f The blanket removal of isolation in default-value expressions had unintented consequences for important workflows. It's still a problem that needs to be addressed, but we need to be more precise about the problematic situations. --- include/swift/AST/DiagnosticsSema.def | 4 -- lib/AST/Decl.cpp | 19 +----- lib/Sema/TypeCheckConcurrency.cpp | 61 +------------------ test/Concurrency/global_actor_inference.swift | 6 +- .../property_initializers_swift6.swift | 4 +- .../Sema/SwiftUI/radar88971160.swift | 17 ++++++ 6 files changed, 26 insertions(+), 85 deletions(-) create mode 100644 validation-test/Sema/SwiftUI/radar88971160.swift diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 854ef71461e69..0f0f296c691dd 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -4555,10 +4555,6 @@ ERROR(global_actor_from_nonactor_context,none, "%0 %1 isolated to global actor %2 can not be %select{referenced|mutated|used 'inout'}4" " from %select{this|a non-isolated}3%select{| synchronous}5 context", (DescriptiveDeclKind, DeclName, Type, bool, unsigned, bool)) -ERROR(global_actor_from_initializing_expr,none, - "expression requiring global actor %0 cannot appear in " - "default-value expression of %1 %2", - (Type, DescriptiveDeclKind, DeclName)) ERROR(actor_isolated_call,none, "call to %0 function in a synchronous %1 context", (ActorIsolation, ActorIsolation)) diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index a69f4a2c482d3..2b11a2c2abc8c 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -9044,24 +9044,9 @@ ActorIsolation swift::getActorIsolationOfContext(DeclContext *dc) { if (auto *vd = dyn_cast_or_null(dc->getAsDecl())) return getActorIsolation(vd); - // In the context of the initializing or default-value expression of a - // stored property, the isolation varies between global and type members: - // - For a static stored property, the isolation matches the VarDecl. - // - For a field of a nominal type, the expression is not isolated. - // Without this distinction, a nominal can have non-async initializers - // with various kinds of isolation, so an impossible constraint can be - // created. See SE-0327 for details. - if (auto *var = dc->getNonLocalVarDecl()) { - - // Isolation officially changes, as described above, in Swift 6+ - if (dc->getASTContext().isSwiftVersionAtLeast(6) && - var->isInstanceMember() && - !var->getAttrs().hasAttribute()) { - return ActorIsolation::forUnspecified(); - } - + if (auto *var = dc->getNonLocalVarDecl()) return getActorIsolation(var); - } + if (auto *closure = dyn_cast(dc)) { switch (auto isolation = closure->getActorIsolation()) { diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index e0bff4cc03ae3..bd937e26ed51b 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1396,57 +1396,6 @@ namespace { return getDeclContext()->getParentModule(); } - /// In Swift 6, global-actor isolation is not carried-over to the - /// initializing expressions of non-static instance properties. - /// The actual change happens in \c getActorIsolationOfContext , - /// but this function exists to warn users of Swift 5 about this - /// isolation change, so that they can prepare ahead-of-time. - void warnAboutGlobalActorIsoChangeInSwift6(const ActorIsolation &reqIso, - const Expr *user) { - if (ctx.isSwiftVersionAtLeast(6)) - return; - - // Check our context stack for a PatternBindingInitializer environment. - DeclContext const* withinDC = nullptr; - for (auto dc = contextStack.rbegin(); dc != contextStack.rend(); dc++) { - if (isa(*dc)) { - withinDC = *dc; - break; - } - } - - // Not within a relevant decl context. - if (!withinDC) - return; - - // Check if this PatternBindingInitializer's isolation would change - // in Swift 6+ - if (auto *var = withinDC->getNonLocalVarDecl()) { - if (var->isInstanceMember() && - !var->getAttrs().hasAttribute()) { - // At this point, we know the isolation will change in Swift 6. - // So, let's check if that change will cause an error. - - auto dcIso = getActorIsolationOfContext( - const_cast(withinDC)); - - // If the isolation granted in Swift 5 is for a global actor, and - // the expression requires that global actor's isolation, then it will - // become an error in Swift 6. - if (dcIso.isGlobalActor() && dcIso == reqIso) { - ctx.Diags.diagnose(user->getLoc(), - diag::global_actor_from_initializing_expr, - reqIso.getGlobalActor(), - var->getDescriptiveKind(), var->getName()) - .highlight(user->getSourceRange()) - // make it a warning and attach the "this will become an error..." - // to the message. The error in Swift 6 will not be this diagnostic. - .warnUntilSwiftVersion(6); - } - } - } - } - /// Determine whether code in the given use context might execute /// concurrently with code in the definition context. bool mayExecuteConcurrentlyWith( @@ -2341,12 +2290,8 @@ namespace { // we are within that global actor already. Optional unsatisfiedIsolation; if (Type globalActor = fnType->getGlobalActor()) { - if (getContextIsolation().isGlobalActor() && - getContextIsolation().getGlobalActor()->isEqual(globalActor)) { - warnAboutGlobalActorIsoChangeInSwift6( - ActorIsolation::forGlobalActor(globalActor, false), - apply); - } else { + if (!(getContextIsolation().isGlobalActor() && + getContextIsolation().getGlobalActor()->isEqual(globalActor))) { unsatisfiedIsolation = ActorIsolation::forGlobalActor( globalActor, /*unsafe=*/false); } @@ -2483,8 +2428,6 @@ namespace { auto contextIsolation = getInnermostIsolatedContext(declContext); if (contextIsolation.isGlobalActor() && contextIsolation.getGlobalActor()->isEqual(globalActor)) { - - warnAboutGlobalActorIsoChangeInSwift6(contextIsolation, context); return false; } diff --git a/test/Concurrency/global_actor_inference.swift b/test/Concurrency/global_actor_inference.swift index 0c9154648c804..aa4629d0d7e3a 100644 --- a/test/Concurrency/global_actor_inference.swift +++ b/test/Concurrency/global_actor_inference.swift @@ -623,9 +623,9 @@ func replacesDynamicOnMainActor() { // ---------------------------------------------------------------------- class Cutter { - @MainActor var x = useFooInADefer() // expected-warning {{expression requiring global actor 'MainActor' cannot appear in default-value expression of property 'x'; this is an error in Swift 6}} + @MainActor var x = useFooInADefer() @MainActor var y = { () -> Bool in - var z = statefulThingy // expected-warning {{expression requiring global actor 'MainActor' cannot appear in default-value expression of property 'y'; this is an error in Swift 6}} + var z = statefulThingy return z }() } @@ -637,7 +637,7 @@ class Butter { nonisolated let b = statefulThingy // expected-error {{var 'statefulThingy' isolated to global actor 'MainActor' can not be referenced from a non-isolated synchronous context}} var c: Int = { - return getGlobal7() // expected-warning {{expression requiring global actor 'SomeGlobalActor' cannot appear in default-value expression of property 'c'; this is an error in Swift 6}} + return getGlobal7() }() lazy var d: Int = getGlobal7() diff --git a/test/Concurrency/property_initializers_swift6.swift b/test/Concurrency/property_initializers_swift6.swift index 21bf58638dbe5..5811c2be213a1 100644 --- a/test/Concurrency/property_initializers_swift6.swift +++ b/test/Concurrency/property_initializers_swift6.swift @@ -9,11 +9,11 @@ actor GlobalActor { } @GlobalActor -func globalActorFn() -> Int { return 0 } // expected-note 2 {{calls to global function 'globalActorFn()' from outside of its actor context are implicitly asynchronous}} +func globalActorFn() -> Int { return 0 } // expected-note {{calls to global function 'globalActorFn()' from outside of its actor context are implicitly asynchronous}} @GlobalActor class C { - var x: Int = globalActorFn() // expected-error {{call to global actor 'GlobalActor'-isolated global function 'globalActorFn()' in a synchronous nonisolated context}} + var x: Int = globalActorFn() lazy var y: Int = globalActorFn() diff --git a/validation-test/Sema/SwiftUI/radar88971160.swift b/validation-test/Sema/SwiftUI/radar88971160.swift new file mode 100644 index 0000000000000..4f78c8b1bab6d --- /dev/null +++ b/validation-test/Sema/SwiftUI/radar88971160.swift @@ -0,0 +1,17 @@ +// RUN: %target-typecheck-verify-swift -target %target-cpu-apple-macosx10.15 -swift-version 5 +// REQUIRES: objc_interop +// REQUIRES: OS=macosx + +import SwiftUI + +@MainActor +class ContentState: ObservableObject { } + +struct SomeView: View { + @StateObject private var contentState = ContentState() + + var body: some View { + Text("Hello, world!") + } +} + From 6a5d51dc2a504913e710b1dec5316188ca6d6994 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 18 Mar 2022 22:51:59 -0400 Subject: [PATCH 224/242] Sema: Fix structural resolution of protocol typealiases with UnboundGenericType We want to resolve them to their real underlying type here instead of returning a DependentMemberType. --- lib/Sema/TypeCheckType.cpp | 3 ++- test/Generics/protocol_typealias_cycle_4.swift | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 test/Generics/protocol_typealias_cycle_4.swift diff --git a/lib/Sema/TypeCheckType.cpp b/lib/Sema/TypeCheckType.cpp index 81341c76a5d8a..9373076b2e0fc 100644 --- a/lib/Sema/TypeCheckType.cpp +++ b/lib/Sema/TypeCheckType.cpp @@ -433,7 +433,8 @@ Type TypeResolution::resolveTypeInContext(TypeDecl *typeDecl, if (typeDecl->getDeclContext()->getSelfProtocolDecl()) { if (isa(typeDecl) || (isa(typeDecl) && - !cast(typeDecl)->isGeneric())) { + !cast(typeDecl)->isGeneric() && + !isSpecialized)) { if (getStage() == TypeResolutionStage::Structural) { return DependentMemberType::get(selfType, typeDecl->getName()); } else if (auto assocType = dyn_cast(typeDecl)) { diff --git a/test/Generics/protocol_typealias_cycle_4.swift b/test/Generics/protocol_typealias_cycle_4.swift new file mode 100644 index 0000000000000..418b3ede6710b --- /dev/null +++ b/test/Generics/protocol_typealias_cycle_4.swift @@ -0,0 +1,12 @@ +// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s + +// CHECK-LABEL: .P@ +// CHECK-NEXT: Requirement signature: > +protocol P { + typealias A = _A + typealias B = A + + associatedtype X where X == B +} + +struct _A {} From d0ee43c87057587f1f6935870a065b5b849d7eef Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Sat, 19 Mar 2022 14:25:48 +0900 Subject: [PATCH 225/242] Disable test while we investigte --- test/Distributed/Runtime/distributed_actor_localSystem.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/Distributed/Runtime/distributed_actor_localSystem.swift b/test/Distributed/Runtime/distributed_actor_localSystem.swift index 83ea6d8b2e3af..dc0b28d608aea 100644 --- a/test/Distributed/Runtime/distributed_actor_localSystem.swift +++ b/test/Distributed/Runtime/distributed_actor_localSystem.swift @@ -13,6 +13,9 @@ // rdar://90373022 // UNSUPPORTED: OS=watchos +// FIXME(distributed): seems to fail also on simulator on x86_64? +// REQUIRES: rdar90373022 + import Distributed distributed actor Worker { From 17bcf79e0075a56ad3f284e0d7e4d4daee5fcd3e Mon Sep 17 00:00:00 2001 From: Allan Shortlidge Date: Sat, 19 Mar 2022 09:50:07 -0700 Subject: [PATCH 226/242] Tests: Temporarily disable attr_backDeploy_evolution during swift_test_mode_optimize builds. --- test/attr/attr_backDeploy_evolution.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/attr/attr_backDeploy_evolution.swift b/test/attr/attr_backDeploy_evolution.swift index 27f314af07bd1..d5bbc43b21943 100644 --- a/test/attr/attr_backDeploy_evolution.swift +++ b/test/attr/attr_backDeploy_evolution.swift @@ -32,6 +32,9 @@ // REQUIRES: executable_test // REQUIRES: VENDOR=apple +// rdar://90525337 +// UNSUPPORTED: swift_test_mode_optimize + // ---- (0) Prepare SDK // RUN: %empty-directory(%t) // RUN: %empty-directory(%t/SDK_ABI) From 2e3aa87737d0ca7b3dd0f10a2abbe7d4df4ffc7c Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Sat, 19 Mar 2022 13:36:28 -0700 Subject: [PATCH 227/242] Revert "Revert "Merge pull request #41831 from hyp/unify-header"" This reverts commit 4c9582c29507987591742e5019f46e43f9319add. --- include/swift/AST/DiagnosticsFrontend.def | 4 +- include/swift/Basic/FileTypes.def | 3 +- .../swift/Basic/SupplementaryOutputPaths.h | 29 ++---- include/swift/Frontend/Frontend.h | 3 +- .../swift/Frontend/FrontendInputsAndOutputs.h | 3 +- include/swift/Option/Options.td | 10 +- include/swift/PrintAsClang/PrintAsClang.h | 16 ++-- lib/Basic/FileTypes.cpp | 9 +- lib/Driver/Driver.cpp | 14 ++- lib/Driver/ToolChains.cpp | 12 +-- .../ArgsToFrontendOptionsConverter.cpp | 7 +- .../ArgsToFrontendOutputsConverter.cpp | 50 ++++------ lib/Frontend/Frontend.cpp | 9 +- lib/Frontend/FrontendInputsAndOutputs.cpp | 12 +-- lib/Frontend/FrontendOptions.cpp | 3 +- lib/FrontendTool/FrontendTool.cpp | 40 ++------ lib/PrintAsClang/PrintAsClang.cpp | 91 ++++++++++--------- test/Driver/bridging-pch.swift | 6 +- test/Driver/parseable_output.swift | 2 +- test/Driver/parseable_output_unicode.swift | 2 +- .../supplementary-output-support.swift | 13 +-- .../SwiftToCxx/functions/cdecl-execution.cpp | 2 +- test/Interop/SwiftToCxx/functions/cdecl.swift | 2 +- .../functions/function-availability.swift | 2 +- .../functions/swift-functions-execution.cpp | 2 +- .../functions/swift-functions.swift | 2 +- .../module/module-to-namespace.swift | 2 +- test/PrintAsCxx/empty.swift | 24 +++-- .../Inputs/comments-expected-output.h | 3 + 29 files changed, 157 insertions(+), 220 deletions(-) diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index d9d48be606cb4..e834a11bd02a1 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -118,9 +118,7 @@ ERROR(error_mode_cannot_emit_dependencies,none, ERROR(error_mode_cannot_emit_reference_dependencies,none, "this mode does not support emitting reference dependency files", ()) ERROR(error_mode_cannot_emit_header,none, - "this mode does not support emitting Objective-C headers", ()) -ERROR(error_mode_cannot_emit_cxx_header,none, - "this mode does not support emitting C++ headers", ()) + "this mode does not support emitting Objective-C or C++ headers", ()) ERROR(error_mode_cannot_emit_loaded_module_trace,none, "this mode does not support emitting the loaded module trace", ()) ERROR(error_mode_cannot_emit_module,none, diff --git a/include/swift/Basic/FileTypes.def b/include/swift/Basic/FileTypes.def index b4490c07c30be..2d3c7e3754332 100644 --- a/include/swift/Basic/FileTypes.def +++ b/include/swift/Basic/FileTypes.def @@ -59,8 +59,7 @@ TYPE("raw-sib", RawSIB, "sib", "") TYPE("llvm-ir", LLVM_IR, "ll", "") TYPE("llvm-bc", LLVM_BC, "bc", "") TYPE("diagnostics", SerializedDiagnostics, "dia", "") -TYPE("objc-header", ObjCHeader, "h", "") -TYPE("cxx-header", CXXHeader, "h", "") +TYPE("clang-header", ClangHeader, "h", "") TYPE("swift-dependencies", SwiftDeps, "swiftdeps", "") TYPE("external-swift-dependencies", ExternalSwiftDeps, "swiftdeps.external", "") TYPE("remap", Remapping, "remap", "") diff --git a/include/swift/Basic/SupplementaryOutputPaths.h b/include/swift/Basic/SupplementaryOutputPaths.h index 68eb39d3d8fba..c8ba0b075ba0e 100644 --- a/include/swift/Basic/SupplementaryOutputPaths.h +++ b/include/swift/Basic/SupplementaryOutputPaths.h @@ -20,7 +20,8 @@ namespace swift { struct SupplementaryOutputPaths { - /// The path to which we should emit an Objective-C header for the module. + /// The path to which we should emit a header file that exposes the Swift + /// declarations to C, Objective-C and C++ clients for the module. /// /// Currently only makes sense when the compiler has whole module knowledge. /// The modes for which it makes sense incuide both WMO and the "merge @@ -28,19 +29,8 @@ struct SupplementaryOutputPaths { /// the header is emitted in single-file mode, since it needs whole-module /// information. /// - /// \sa swift::printAsObjC - std::string ObjCHeaderOutputPath; - - /// The path to which we should emit a C++ header for the module. - /// - /// Currently only makes sense when the compiler has whole module knowledge. - /// The modes for which it makes sense include both WMO and the "merge - /// modules" job that happens after the normal compilation jobs. That's where - /// the header is emitted in single-file mode, since it needs whole-module - /// information. - /// - /// \sa swift::printAsCXX - std::string CxxHeaderOutputPath; + /// \sa swift::printAsClangHeader + std::string ClangHeaderOutputPath; /// The path to which we should emit a serialized module. /// It is valid whenever there are any inputs. @@ -170,10 +160,8 @@ struct SupplementaryOutputPaths { /// Apply a given function for each existing (non-empty string) supplementary output void forEachSetOutput(llvm::function_ref fn) const { - if (!ObjCHeaderOutputPath.empty()) - fn(ObjCHeaderOutputPath); - if (!CxxHeaderOutputPath.empty()) - fn(CxxHeaderOutputPath); + if (!ClangHeaderOutputPath.empty()) + fn(ClangHeaderOutputPath); if (!ModuleOutputPath.empty()) fn(ModuleOutputPath); if (!ModuleSourceInfoOutputPath.empty()) @@ -209,9 +197,8 @@ struct SupplementaryOutputPaths { } bool empty() const { - return ObjCHeaderOutputPath.empty() && CxxHeaderOutputPath.empty() && - ModuleOutputPath.empty() && ModuleDocOutputPath.empty() && - DependenciesFilePath.empty() && + return ClangHeaderOutputPath.empty() && ModuleOutputPath.empty() && + ModuleDocOutputPath.empty() && DependenciesFilePath.empty() && ReferenceDependenciesFilePath.empty() && SerializedDiagnosticsPath.empty() && LoadedModuleTracePath.empty() && TBDPath.empty() && ModuleInterfaceOutputPath.empty() && diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 93bfe7376f7b5..2a35e7c822a15 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -392,8 +392,7 @@ class CompilerInvocation { std::string getOutputFilenameForAtMostOnePrimary() const; std::string getMainInputFilenameForDebugInfoForAtMostOnePrimary() const; - std::string getObjCHeaderOutputPathForAtMostOnePrimary() const; - std::string getCxxHeaderOutputPathForAtMostOnePrimary() const; + std::string getClangHeaderOutputPathForAtMostOnePrimary() const; std::string getModuleOutputPathForAtMostOnePrimary() const; std::string getReferenceDependenciesFilePathForPrimary(StringRef filename) const; diff --git a/include/swift/Frontend/FrontendInputsAndOutputs.h b/include/swift/Frontend/FrontendInputsAndOutputs.h index 572f3a1df17b4..c28810fb5c84f 100644 --- a/include/swift/Frontend/FrontendInputsAndOutputs.h +++ b/include/swift/Frontend/FrontendInputsAndOutputs.h @@ -249,8 +249,7 @@ class FrontendInputsAndOutputs { bool hasDependenciesPath() const; bool hasReferenceDependenciesPath() const; - bool hasObjCHeaderOutputPath() const; - bool hasCxxHeaderOutputPath() const; + bool hasClangHeaderOutputPath() const; bool hasLoadedModuleTracePath() const; bool hasModuleOutputPath() const; bool hasModuleDocOutputPath() const; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index e8787c5074617..faae3e2a0db28 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -532,13 +532,11 @@ def emit_objc_header_path : Separate<["-"], "emit-objc-header-path">, SupplementaryOutput]>, MetaVarName<"">, HelpText<"Emit an Objective-C header file to ">; -def emit_cxx_header : Flag<["-"], "emit-cxx-header">, - Flags<[FrontendOption, NoInteractiveOption, SupplementaryOutput]>, - HelpText<"Emit a C++ header file">; -def emit_cxx_header_path : Separate<["-"], "emit-cxx-header-path">, - Flags<[FrontendOption, NoInteractiveOption, ArgumentIsPath, +def emit_clang_header_path : Separate<["-"], "emit-clang-header-path">, + Flags<[FrontendOption, NoDriverOption, NoInteractiveOption, ArgumentIsPath, SupplementaryOutput]>, - MetaVarName<"">, HelpText<"Emit a C++ header file to ">; + HelpText<"Emit an Objective-C and C++ header file to ">, + Alias; def static : Flag<["-"], "static">, Flags<[FrontendOption, ModuleInterfaceOption, NoInteractiveOption]>, diff --git a/include/swift/PrintAsClang/PrintAsClang.h b/include/swift/PrintAsClang/PrintAsClang.h index 867f75fd245e8..a52699b330dc4 100644 --- a/include/swift/PrintAsClang/PrintAsClang.h +++ b/include/swift/PrintAsClang/PrintAsClang.h @@ -21,16 +21,18 @@ namespace swift { class ModuleDecl; class ValueDecl; - /// Print the Objective-C-compatible declarations in a module as a Clang - /// header. + /// Print the exposed declarations in a module into a Clang header. /// - /// Returns true on error. - bool printAsObjC(raw_ostream &out, ModuleDecl *M, StringRef bridgingHeader); - - /// Print the C++-compatible declarations in a module as a Clang header. + /// The Objective-C compatible declarations are printed into a block that + /// ensures that those declarations are only usable when the header is + /// compiled in Objective-C mode. + /// The C++ compatible declarations are printed into a block that ensures + /// that those declarations are only usable when the header is compiled in + /// C++ mode. /// /// Returns true on error. - bool printAsCXX(raw_ostream &os, ModuleDecl *M); + bool printAsClangHeader(raw_ostream &out, ModuleDecl *M, + StringRef bridgingHeader); } #endif diff --git a/lib/Basic/FileTypes.cpp b/lib/Basic/FileTypes.cpp index e8d6f5c50ec7c..607c4cece5879 100644 --- a/lib/Basic/FileTypes.cpp +++ b/lib/Basic/FileTypes.cpp @@ -73,8 +73,7 @@ bool file_types::isTextual(ID Id) { case file_types::TY_ASTDump: case file_types::TY_RawSIL: case file_types::TY_LLVM_IR: - case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: + case file_types::TY_ClangHeader: case file_types::TY_AutolinkFile: case file_types::TY_ImportedModules: case file_types::TY_TBD: @@ -132,8 +131,7 @@ bool file_types::isAfterLLVM(ID Id) { case file_types::TY_Dependencies: case file_types::TY_ASTDump: case file_types::TY_RawSIL: - case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: + case file_types::TY_ClangHeader: case file_types::TY_AutolinkFile: case file_types::TY_Image: case file_types::TY_dSYM: @@ -183,8 +181,7 @@ bool file_types::isPartOfSwiftCompilation(ID Id) { case file_types::TY_LLVM_BC: case file_types::TY_Object: case file_types::TY_Dependencies: - case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: + case file_types::TY_ClangHeader: case file_types::TY_AutolinkFile: case file_types::TY_PCH: case file_types::TY_ImportedModules: diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index 02d9cf654016c..a7359bb9c9ac9 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -1981,8 +1981,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, if (Arg *A = Args.getLastArg(options::OPT_import_objc_header)) { StringRef Value = A->getValue(); auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value)); - if (Ty == file_types::TY_ObjCHeader || - Ty == file_types::TY_CXXHeader) { + if (Ty == file_types::TY_ClangHeader) { auto *HeaderInput = C.createAction(*A, Ty); StringRef PersistentPCHDir; if (const Arg *A = Args.getLastArg(options::OPT_pch_output_dir)) { @@ -2065,8 +2064,7 @@ void Driver::buildActions(SmallVectorImpl &TopLevelActions, case file_types::TY_LLVM_IR: case file_types::TY_LLVM_BC: case file_types::TY_SerializedDiagnostics: - case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: + case file_types::TY_ClangHeader: case file_types::TY_ClangModuleFile: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -3480,12 +3478,12 @@ void Driver::chooseObjectiveCHeaderOutputPath(Compilation &C, StringRef workingDirectory, CommandOutput *Output) const { - if (hasExistingAdditionalOutput(*Output, file_types::TY_ObjCHeader)) + if (hasExistingAdditionalOutput(*Output, file_types::TY_ClangHeader)) return; StringRef ObjCHeaderPath; if (OutputMap) { - auto iter = OutputMap->find(file_types::TY_ObjCHeader); + auto iter = OutputMap->find(file_types::TY_ClangHeader); if (iter != OutputMap->end()) ObjCHeaderPath = iter->second; } @@ -3495,13 +3493,13 @@ void Driver::chooseObjectiveCHeaderOutputPath(Compilation &C, ObjCHeaderPath = A->getValue(); if (!ObjCHeaderPath.empty()) { - Output->setAdditionalOutputForType(file_types::TY_ObjCHeader, + Output->setAdditionalOutputForType(file_types::TY_ClangHeader, ObjCHeaderPath); } else { // Put the header next to the primary output file. // FIXME: That's not correct if the user /just/ passed -emit-header // and not -emit-module. - addAuxiliaryOutput(C, *Output, file_types::TY_ObjCHeader, + addAuxiliaryOutput(C, *Output, file_types::TY_ClangHeader, /*output file map*/ nullptr, workingDirectory); } } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index aaa7a6b7299c8..62b6ddc211d5f 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -675,8 +675,7 @@ const char *ToolChain::JobContext::computeFrontendModeForCompile() const { case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: case file_types::TY_SerializedDiagnostics: - case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: + case file_types::TY_ClangHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -815,7 +814,7 @@ void ToolChain::JobContext::addFrontendSupplementaryOutputArguments( file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); - if (addOutputsOfType(arguments, Output, Args, file_types::ID::TY_ObjCHeader, + if (addOutputsOfType(arguments, Output, Args, file_types::ID::TY_ClangHeader, "-emit-objc-header-path")) { assert(OI.CompilerMode == OutputInfo::Mode::SingleCompile && "The Swift tool should only emit an Obj-C header in single compile" @@ -936,8 +935,7 @@ ToolChain::constructInvocation(const BackendJobAction &job, case file_types::TY_Dependencies: case file_types::TY_SwiftModuleDocFile: case file_types::TY_SerializedDiagnostics: - case file_types::TY_ObjCHeader: - case file_types::TY_CXXHeader: + case file_types::TY_ClangHeader: case file_types::TY_Image: case file_types::TY_SwiftDeps: case file_types::TY_ExternalSwiftDeps: @@ -1100,7 +1098,7 @@ ToolChain::constructInvocation(const MergeModuleJobAction &job, file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); addOutputsOfType(Arguments, context.Output, context.Args, - file_types::TY_ObjCHeader, "-emit-objc-header-path"); + file_types::TY_ClangHeader, "-emit-objc-header-path"); addOutputsOfType(Arguments, context.Output, context.Args, file_types::TY_TBD, "-emit-tbd-path"); @@ -1309,7 +1307,7 @@ ToolChain::constructInvocation(const GeneratePCHJobAction &job, file_types::TY_SerializedDiagnostics, "-serialize-diagnostics-path"); - addInputsOfType(Arguments, context.InputActions, file_types::TY_ObjCHeader); + addInputsOfType(Arguments, context.InputActions, file_types::TY_ClangHeader); context.Args.AddLastArg(Arguments, options::OPT_index_store_path); if (job.isPersistentPCH()) { diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 0526af38d1a3f..02071f42c0c8d 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -625,15 +625,10 @@ bool ArgsToFrontendOptionsConverter::checkUnusedSupplementaryOutputPaths() return true; } if (!FrontendOptions::canActionEmitClangHeader(Opts.RequestedAction) && - Opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { + Opts.InputsAndOutputs.hasClangHeaderOutputPath()) { Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_header); return true; } - if (!FrontendOptions::canActionEmitClangHeader(Opts.RequestedAction) && - Opts.InputsAndOutputs.hasCxxHeaderOutputPath()) { - Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_cxx_header); - return true; - } if (!FrontendOptions::canActionEmitLoadedModuleTrace(Opts.RequestedAction) && Opts.InputsAndOutputs.hasLoadedModuleTracePath()) { Diags.diagnose(SourceLoc(), diff --git a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp index 7095d391129d6..1ebaa9837f3cd 100644 --- a/lib/Frontend/ArgsToFrontendOutputsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOutputsConverter.cpp @@ -308,10 +308,8 @@ Optional> SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() const { - auto objCHeaderOutput = getSupplementaryFilenamesFromArguments( + auto clangHeaderOutput = getSupplementaryFilenamesFromArguments( options::OPT_emit_objc_header_path); - auto cxxHeaderOutput = - getSupplementaryFilenamesFromArguments(options::OPT_emit_cxx_header_path); auto moduleOutput = getSupplementaryFilenamesFromArguments(options::OPT_emit_module_path); auto moduleDocOutput = @@ -341,8 +339,8 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() options::OPT_emit_module_semantic_info_path); auto optRecordOutput = getSupplementaryFilenamesFromArguments( options::OPT_save_optimization_record_path); - if (!objCHeaderOutput || !cxxHeaderOutput || !moduleOutput || - !moduleDocOutput || !dependenciesFile || !referenceDependenciesFile || + if (!clangHeaderOutput || !moduleOutput || !moduleDocOutput || + !dependenciesFile || !referenceDependenciesFile || !serializedDiagnostics || !fixItsOutput || !loadedModuleTrace || !TBD || !moduleInterfaceOutput || !privateModuleInterfaceOutput || !moduleSourceInfoOutput || !moduleSummaryOutput || !abiDescriptorOutput || @@ -355,8 +353,7 @@ SupplementaryOutputPathsComputer::getSupplementaryOutputPathsFromArguments() InputsAndOutputs.countOfFilesProducingSupplementaryOutput(); for (unsigned i = 0; i < N; ++i) { SupplementaryOutputPaths sop; - sop.ObjCHeaderOutputPath = (*objCHeaderOutput)[i]; - sop.CxxHeaderOutputPath = (*cxxHeaderOutput)[i]; + sop.ClangHeaderOutputPath = (*clangHeaderOutput)[i]; sop.ModuleOutputPath = (*moduleOutput)[i]; sop.ModuleDocOutputPath = (*moduleDocOutput)[i]; sop.DependenciesFilePath = (*dependenciesFile)[i]; @@ -437,14 +434,9 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( // There is no non-path form of -emit-fixits-path auto fixItsOutputPath = pathsFromArguments.FixItsOutputPath; - auto objcHeaderOutputPath = determineSupplementaryOutputFilename( - OPT_emit_objc_header, pathsFromArguments.ObjCHeaderOutputPath, - file_types::TY_ObjCHeader, "", - defaultSupplementaryOutputPathExcludingExtension); - - auto cxxHeaderOutputPath = determineSupplementaryOutputFilename( - OPT_emit_cxx_header, pathsFromArguments.CxxHeaderOutputPath, - file_types::TY_CXXHeader, "", + auto clangHeaderOutputPath = determineSupplementaryOutputFilename( + OPT_emit_objc_header, pathsFromArguments.ClangHeaderOutputPath, + file_types::TY_ClangHeader, "", defaultSupplementaryOutputPathExcludingExtension); auto loadedModuleTracePath = determineSupplementaryOutputFilename( @@ -500,8 +492,7 @@ SupplementaryOutputPathsComputer::computeOutputPathsForOneInput( defaultSupplementaryOutputPathExcludingExtension); SupplementaryOutputPaths sop; - sop.ObjCHeaderOutputPath = objcHeaderOutputPath; - sop.CxxHeaderOutputPath = cxxHeaderOutputPath; + sop.ClangHeaderOutputPath = clangHeaderOutputPath; sop.ModuleOutputPath = moduleOutputPath; sop.ModuleDocOutputPath = moduleDocOutputPath; sop.DependenciesFilePath = dependenciesFilePath; @@ -586,8 +577,7 @@ createFromTypeToPathMap(const TypeToPathMap *map) { if (!map) return paths; const std::pair typesAndStrings[] = { - {file_types::TY_ObjCHeader, paths.ObjCHeaderOutputPath}, - {file_types::TY_CXXHeader, paths.CxxHeaderOutputPath}, + {file_types::TY_ClangHeader, paths.ClangHeaderOutputPath}, {file_types::TY_SwiftModuleFile, paths.ModuleOutputPath}, {file_types::TY_SwiftModuleDocFile, paths.ModuleDocOutputPath}, {file_types::TY_SwiftSourceInfoFile, paths.ModuleSourceInfoOutputPath}, @@ -615,17 +605,17 @@ createFromTypeToPathMap(const TypeToPathMap *map) { Optional> SupplementaryOutputPathsComputer::readSupplementaryOutputFileMap() const { - if (Arg *A = Args.getLastArg( - options::OPT_emit_objc_header_path, options::OPT_emit_cxx_header_path, - options::OPT_emit_module_path, options::OPT_emit_module_doc_path, - options::OPT_emit_dependencies_path, - options::OPT_emit_reference_dependencies_path, - options::OPT_serialize_diagnostics_path, - options::OPT_emit_loaded_module_trace_path, - options::OPT_emit_module_interface_path, - options::OPT_emit_private_module_interface_path, - options::OPT_emit_module_source_info_path, - options::OPT_emit_tbd_path)) { + if (Arg *A = Args.getLastArg(options::OPT_emit_objc_header_path, + options::OPT_emit_module_path, + options::OPT_emit_module_doc_path, + options::OPT_emit_dependencies_path, + options::OPT_emit_reference_dependencies_path, + options::OPT_serialize_diagnostics_path, + options::OPT_emit_loaded_module_trace_path, + options::OPT_emit_module_interface_path, + options::OPT_emit_private_module_interface_path, + options::OPT_emit_module_source_info_path, + options::OPT_emit_tbd_path)) { Diags.diagnose(SourceLoc(), diag::error_cannot_have_supplementary_outputs, A->getSpelling(), "-supplementary-output-file-map"); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index f8a5ee1b462ed..2fe54499d1fdc 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -92,14 +92,9 @@ CompilerInvocation::getMainInputFilenameForDebugInfoForAtMostOnePrimary() .MainInputFilenameForDebugInfo; } std::string -CompilerInvocation::getObjCHeaderOutputPathForAtMostOnePrimary() const { +CompilerInvocation::getClangHeaderOutputPathForAtMostOnePrimary() const { return getPrimarySpecificPathsForAtMostOnePrimary() - .SupplementaryOutputs.ObjCHeaderOutputPath; -} -std::string -CompilerInvocation::getCxxHeaderOutputPathForAtMostOnePrimary() const { - return getPrimarySpecificPathsForAtMostOnePrimary() - .SupplementaryOutputs.CxxHeaderOutputPath; + .SupplementaryOutputs.ClangHeaderOutputPath; } std::string CompilerInvocation::getModuleOutputPathForAtMostOnePrimary() const { return getPrimarySpecificPathsForAtMostOnePrimary() diff --git a/lib/Frontend/FrontendInputsAndOutputs.cpp b/lib/Frontend/FrontendInputsAndOutputs.cpp index 59d12af5c142a..cb6b4ad2dc515 100644 --- a/lib/Frontend/FrontendInputsAndOutputs.cpp +++ b/lib/Frontend/FrontendInputsAndOutputs.cpp @@ -213,7 +213,7 @@ bool FrontendInputsAndOutputs::shouldTreatAsObjCHeader() const { if (hasSingleInput()) { StringRef InputExt = llvm::sys::path::extension(getFilenameOfFirstInput()); switch (file_types::lookupTypeForExtension(InputExt)) { - case file_types::TY_ObjCHeader: + case file_types::TY_ClangHeader: return true; default: return false; @@ -461,16 +461,10 @@ bool FrontendInputsAndOutputs::hasReferenceDependenciesPath() const { return outs.ReferenceDependenciesFilePath; }); } -bool FrontendInputsAndOutputs::hasObjCHeaderOutputPath() const { +bool FrontendInputsAndOutputs::hasClangHeaderOutputPath() const { return hasSupplementaryOutputPath( [](const SupplementaryOutputPaths &outs) -> const std::string & { - return outs.ObjCHeaderOutputPath; - }); -} -bool FrontendInputsAndOutputs::hasCxxHeaderOutputPath() const { - return hasSupplementaryOutputPath( - [](const SupplementaryOutputPaths &outs) -> const std::string & { - return outs.CxxHeaderOutputPath; + return outs.ClangHeaderOutputPath; }); } bool FrontendInputsAndOutputs::hasLoadedModuleTracePath() const { diff --git a/lib/Frontend/FrontendOptions.cpp b/lib/Frontend/FrontendOptions.cpp index d956b71d203c1..84ecf13f446bb 100644 --- a/lib/Frontend/FrontendOptions.cpp +++ b/lib/Frontend/FrontendOptions.cpp @@ -234,8 +234,7 @@ void FrontendOptions::forAllOutputPaths( const std::string *outputs[] = { &outs.ModuleOutputPath, &outs.ModuleDocOutputPath, &outs.ModuleInterfaceOutputPath, &outs.PrivateModuleInterfaceOutputPath, - &outs.ObjCHeaderOutputPath, &outs.CxxHeaderOutputPath, - &outs.ModuleSourceInfoOutputPath}; + &outs.ClangHeaderOutputPath, &outs.ModuleSourceInfoOutputPath}; for (const std::string *next : outputs) { if (!next->empty()) fn(*next); diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 095c3662e4084..9317a20864f24 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -170,36 +170,22 @@ static bool writeSIL(SILModule &SM, const PrimarySpecificPaths &PSPs, /// Prints the Objective-C "generated header" interface for \p M to \p /// outputPath. +/// Print the exposed "generated header" interface for \p M to \p +/// outputPath. /// /// ...unless \p outputPath is empty, in which case it does nothing. /// /// \returns true if there were any errors /// -/// \see swift::printAsObjC -static bool printAsObjCIfNeeded(StringRef outputPath, ModuleDecl *M, - StringRef bridgingHeader) { +/// \see swift::printAsClangHeader +static bool printAsClangHeaderIfNeeded(StringRef outputPath, ModuleDecl *M, + StringRef bridgingHeader) { if (outputPath.empty()) return false; return withOutputFile(M->getDiags(), outputPath, [&](raw_ostream &out) -> bool { - return printAsObjC(out, M, bridgingHeader); - }); -} - -/// Prints the C++ "generated header" interface for \p M to \p -/// outputPath. -/// -/// ...unless \p outputPath is empty, in which case it does nothing. -/// -/// \returns true if there were any errors -/// -/// \see swift::printAsCXX -static bool printAsCxxIfNeeded(StringRef outputPath, ModuleDecl *M) { - if (outputPath.empty()) - return false; - return withOutputFile( - M->getDiags(), outputPath, - [&](raw_ostream &os) -> bool { return printAsCXX(os, M); }); + return printAsClangHeader(out, M, bridgingHeader); + }); } /// Prints the stable module interface for \p M to \p outputPath. @@ -824,7 +810,7 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( bool hadAnyError = false; if ((!Context.hadError() || opts.AllowModuleWithCompilerErrors) && - opts.InputsAndOutputs.hasObjCHeaderOutputPath()) { + opts.InputsAndOutputs.hasClangHeaderOutputPath()) { std::string BridgingHeaderPathForPrint; if (!opts.ImplicitObjCHeaderPath.empty()) { if (opts.BridgingHeaderDirForPrint.hasValue()) { @@ -838,16 +824,10 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( BridgingHeaderPathForPrint = opts.ImplicitObjCHeaderPath; } } - hadAnyError |= printAsObjCIfNeeded( - Invocation.getObjCHeaderOutputPathForAtMostOnePrimary(), + hadAnyError |= printAsClangHeaderIfNeeded( + Invocation.getClangHeaderOutputPathForAtMostOnePrimary(), Instance.getMainModule(), BridgingHeaderPathForPrint); } - if ((!Context.hadError() || opts.AllowModuleWithCompilerErrors) && - opts.InputsAndOutputs.hasCxxHeaderOutputPath()) { - hadAnyError |= printAsCxxIfNeeded( - Invocation.getCxxHeaderOutputPathForAtMostOnePrimary(), - Instance.getMainModule()); - } // Only want the header if there's been any errors, ie. there's not much // point outputting a swiftinterface for an invalid module diff --git a/lib/PrintAsClang/PrintAsClang.cpp b/lib/PrintAsClang/PrintAsClang.cpp index f6910759a596e..116067978d1e1 100644 --- a/lib/PrintAsClang/PrintAsClang.cpp +++ b/lib/PrintAsClang/PrintAsClang.cpp @@ -26,28 +26,32 @@ using namespace swift; +static void emitCxxConditional(raw_ostream &out, + llvm::function_ref cxxCase, + llvm::function_ref cCase = {}) { + out << "#if defined(__cplusplus)\n"; + cxxCase(); + if (cCase) { + out << "#else\n"; + cCase(); + } + out << "#endif\n"; +} + +static void emitObjCConditional(raw_ostream &out, + llvm::function_ref objcCase, + llvm::function_ref nonObjCCase = {}) { + out << "#if defined(__OBJC__)\n"; + objcCase(); + if (nonObjCCase) { + out << "#else\n"; + nonObjCCase(); + } + out << "#endif\n"; +} + static void writePrologue(raw_ostream &out, ASTContext &ctx, StringRef macroGuard) { - auto emitCxxConditional = [&](llvm::function_ref cxxCase, - llvm::function_ref cCase = {}) { - out << "#if defined(__cplusplus)\n"; - cxxCase(); - if (cCase) { - out << "#else\n"; - cCase(); - } - out << "#endif\n"; - }; - auto emitObjCConditional = [&](llvm::function_ref objcCase, - llvm::function_ref nonObjCCase = {}) { - out << "#if defined(__OBJC__)\n"; - objcCase(); - if (nonObjCCase) { - out << "#else\n"; - nonObjCCase(); - } - out << "#endif\n"; - }; out << "// Generated by " << version::getSwiftFullVersion(ctx.LangOpts.EffectiveLanguageVersion) @@ -77,8 +81,10 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, "#endif\n" "\n" "#pragma clang diagnostic ignored \"-Wauto-import\"\n"; - emitObjCConditional([&] { out << "#include \n"; }); + emitObjCConditional(out, + [&] { out << "#include \n"; }); emitCxxConditional( + out, [&] { out << "#include \n" "#include \n" @@ -276,7 +282,7 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, "#else\n" "# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg)\n" "#endif\n"; - emitObjCConditional([&] { + emitObjCConditional(out, [&] { out << "#if !defined(IBSegueAction)\n" "# define IBSegueAction\n" "#endif\n"; @@ -295,8 +301,9 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx, }; emitMacro("SWIFT_CALL", "__attribute__((swiftcall))"); // SWIFT_NOEXCEPT applies 'noexcept' in C++ mode only. - emitCxxConditional([&] { emitMacro("SWIFT_NOEXCEPT", "noexcept"); }, - [&] { emitMacro("SWIFT_NOEXCEPT"); }); + emitCxxConditional( + out, [&] { emitMacro("SWIFT_NOEXCEPT", "noexcept"); }, + [&] { emitMacro("SWIFT_NOEXCEPT"); }); static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, "need to add SIMD typedefs here if max elements is increased"); } @@ -442,33 +449,33 @@ static std::string computeMacroGuard(const ModuleDecl *M) { return (llvm::Twine(M->getNameStr().upper()) + "_SWIFT_H").str(); } -bool swift::printAsObjC(raw_ostream &os, ModuleDecl *M, - StringRef bridgingHeader) { - llvm::PrettyStackTraceString trace("While generating Objective-C header"); - +static std::string getModuleContentsCxxString(ModuleDecl &M) { SmallPtrSet imports; std::string moduleContentsBuf; llvm::raw_string_ostream moduleContents{moduleContentsBuf}; - printModuleContentsAsObjC(moduleContents, imports, *M); - writePrologue(os, M->getASTContext(), computeMacroGuard(M)); - writeImports(os, imports, *M, bridgingHeader); - writePostImportPrologue(os, *M); - os << moduleContents.str(); - writeEpilogue(os); - - return false; + printModuleContentsAsCxx(moduleContents, imports, M); + return moduleContents.str(); } -bool swift::printAsCXX(raw_ostream &os, ModuleDecl *M) { - llvm::PrettyStackTraceString trace("While generating C++ header"); +bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M, + StringRef bridgingHeader) { + llvm::PrettyStackTraceString trace("While generating Clang header"); SmallPtrSet imports; - std::string moduleContentsBuf; - llvm::raw_string_ostream moduleContents{moduleContentsBuf}; - printModuleContentsAsCxx(moduleContents, imports, *M); + std::string objcModuleContentsBuf; + llvm::raw_string_ostream objcModuleContents{objcModuleContentsBuf}; + printModuleContentsAsObjC(objcModuleContents, imports, *M); writePrologue(os, M->getASTContext(), computeMacroGuard(M)); + emitObjCConditional(os, + [&] { writeImports(os, imports, *M, bridgingHeader); }); writePostImportPrologue(os, *M); - os << moduleContents.str(); + emitObjCConditional(os, [&] { os << objcModuleContents.str(); }); + emitCxxConditional(os, [&] { + // FIXME: Expose Swift with @expose by default. + if (M->getASTContext().LangOpts.EnableCXXInterop) { + os << getModuleContentsCxxString(*M); + } + }); writeEpilogue(os); return false; diff --git a/test/Driver/bridging-pch.swift b/test/Driver/bridging-pch.swift index 4e585f74d7592..8750a013d2e64 100644 --- a/test/Driver/bridging-pch.swift +++ b/test/Driver/bridging-pch.swift @@ -1,7 +1,7 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift -typecheck -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h %s 2>&1 | %FileCheck %s -check-prefix=YESPCHACT -// YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header +// YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header // YESPCHACT: 1: generate-pch, {0}, pch // YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift // YESPCHACT: 3: compile, {2, 1}, none @@ -30,13 +30,13 @@ // Test persistent PCH // RUN: %target-build-swift -typecheck -driver-print-actions -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACT -// PERSISTENT-YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header +// PERSISTENT-YESPCHACT: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header // PERSISTENT-YESPCHACT: 1: generate-pch, {0}, none // PERSISTENT-YESPCHACT: 2: input, "{{.*}}bridging-pch.swift", swift // PERSISTENT-YESPCHACT: 3: compile, {2, 1}, none // RUN: %target-build-swift -c -driver-print-actions -embed-bitcode -import-objc-header %S/Inputs/bridging-header.h -pch-output-dir %t/pch %s 2>&1 | %FileCheck %s -check-prefix=PERSISTENT-YESPCHACTBC -// PERSISTENT-YESPCHACTBC: 0: input, "{{.*}}Inputs/bridging-header.h", objc-header +// PERSISTENT-YESPCHACTBC: 0: input, "{{.*}}Inputs/bridging-header.h", clang-header // PERSISTENT-YESPCHACTBC: 1: generate-pch, {0}, none // PERSISTENT-YESPCHACTBC: 2: input, "{{.*}}bridging-pch.swift", swift // PERSISTENT-YESPCHACTBC: 3: compile, {2, 1}, llvm-bc diff --git a/test/Driver/parseable_output.swift b/test/Driver/parseable_output.swift index b955298cfd568..46458c013c404 100644 --- a/test/Driver/parseable_output.swift +++ b/test/Driver/parseable_output.swift @@ -94,7 +94,7 @@ // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output.swift.tmp.swiftsourceinfo" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "objc-header", +// CHECK-NEXT: "type": "clang-header", // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output.swift.tmp.h" // CHECK-NEXT: } // CHECK-NEXT: ], diff --git a/test/Driver/parseable_output_unicode.swift b/test/Driver/parseable_output_unicode.swift index e9065ee92be91..f8e8925cfe430 100644 --- a/test/Driver/parseable_output_unicode.swift +++ b/test/Driver/parseable_output_unicode.swift @@ -96,7 +96,7 @@ // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output_unicode.swift.tmp.swiftsourceinfo" // CHECK-NEXT: }, // CHECK-NEXT: { -// CHECK-NEXT: "type": "objc-header", +// CHECK-NEXT: "type": "clang-header", // CHECK-NEXT: "path": "{{.*[\\/]}}parseable_output_unicode.swift.tmp.h" // CHECK-NEXT: } // CHECK-NEXT: ], diff --git a/test/Frontend/supplementary-output-support.swift b/test/Frontend/supplementary-output-support.swift index fc34e980ef885..30fa6bf7406ef 100644 --- a/test/Frontend/supplementary-output-support.swift +++ b/test/Frontend/supplementary-output-support.swift @@ -18,18 +18,11 @@ // RESOLVE_IMPORTS_NO_REFERENCE_DEPS: error: this mode does not support emitting reference dependency files{{$}} // RUN: not %target-swift-frontend -parse -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_OBJC_HEADER %s -// PARSE_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}} +// PARSE_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C or C++ headers{{$}} // RUN: not %target-swift-frontend -dump-ast -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=DUMP_NO_OBJC_HEADER %s -// DUMP_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}} +// DUMP_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C or C++ headers{{$}} // RUN: not %target-swift-frontend -resolve-imports -emit-objc-header %s 2>&1 | %FileCheck -check-prefix=RESOLVE_IMPORTS_NO_OBJC_HEADER %s -// RESOLVE_IMPORTS_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C headers{{$}} - -// RUN: not %target-swift-frontend -parse -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_CXX_HEADER %s -// PARSE_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}} -// RUN: not %target-swift-frontend -dump-ast -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=DUMP_NO_CXX_HEADER %s -// DUMP_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}} -// RUN: not %target-swift-frontend -resolve-imports -emit-cxx-header %s 2>&1 | %FileCheck -check-prefix=RESOLVE_IMPORTS_NO_CXX_HEADER %s -// RESOLVE_IMPORTS_NO_CXX_HEADER: error: this mode does not support emitting C++ headers{{$}} +// RESOLVE_IMPORTS_NO_OBJC_HEADER: error: this mode does not support emitting Objective-C or C++ headers{{$}} // RUN: not %target-swift-frontend -parse -emit-module-interface-path %t %s 2>&1 | %FileCheck -check-prefix=PARSE_NO_INTERFACE %s // PARSE_NO_INTERFACE: error: this mode does not support emitting module interface files{{$}} diff --git a/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp b/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp index 130434cbefdd4..ecef6bad7f3a9 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp +++ b/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %S/cdecl.swift -typecheck -module-name CdeclFunctions -emit-cxx-header-path %t/functions.h +// RUN: %target-swift-frontend %S/cdecl.swift -typecheck -module-name CdeclFunctions -enable-cxx-interop -emit-clang-header-path %t/functions.h // RUN: %target-interop-build-clangxx -c %s -I %t -o %t/cdecl-execution.o // RUN: %target-interop-build-swift %S/cdecl.swift -o %t/cdecl-execution -Xlinker %t/cdecl-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain diff --git a/test/Interop/SwiftToCxx/functions/cdecl.swift b/test/Interop/SwiftToCxx/functions/cdecl.swift index 8b1c73585b9d1..94055be7bee29 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl.swift +++ b/test/Interop/SwiftToCxx/functions/cdecl.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name CdeclFunctions -emit-cxx-header-path %t/cdecl.h +// RUN: %target-swift-frontend %s -typecheck -module-name CdeclFunctions -enable-cxx-interop -emit-clang-header-path %t/cdecl.h // RUN: %FileCheck %s < %t/cdecl.h // RUN: %check-interop-cxx-header-in-clang(%t/cdecl.h) diff --git a/test/Interop/SwiftToCxx/functions/function-availability.swift b/test/Interop/SwiftToCxx/functions/function-availability.swift index 54f9dfb1d958e..b3d9f7ca116fb 100644 --- a/test/Interop/SwiftToCxx/functions/function-availability.swift +++ b/test/Interop/SwiftToCxx/functions/function-availability.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Functions -emit-cxx-header-path %t/functions.h +// RUN: %target-swift-frontend %s -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h // RUN: %FileCheck %s < %t/functions.h // RUN: %check-interop-cxx-header-in-clang(%t/functions.h) diff --git a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp index 08a7d0c5475a9..e9c834d7b87eb 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp +++ b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %S/swift-functions.swift -typecheck -module-name Functions -emit-cxx-header-path %t/functions.h +// RUN: %target-swift-frontend %S/swift-functions.swift -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h // RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-functions-execution.o // RUN: %target-interop-build-swift %S/swift-functions.swift -o %t/swift-functions-execution -Xlinker %t/swift-functions-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain diff --git a/test/Interop/SwiftToCxx/functions/swift-functions.swift b/test/Interop/SwiftToCxx/functions/swift-functions.swift index e37e24bff13ce..6b889d20959c0 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions.swift +++ b/test/Interop/SwiftToCxx/functions/swift-functions.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Functions -emit-cxx-header-path %t/functions.h +// RUN: %target-swift-frontend %s -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h // RUN: %FileCheck %s < %t/functions.h // RUN: %check-interop-cxx-header-in-clang(%t/functions.h) diff --git a/test/Interop/SwiftToCxx/module/module-to-namespace.swift b/test/Interop/SwiftToCxx/module/module-to-namespace.swift index eeba2992d63b5..db53e90cd3167 100644 --- a/test/Interop/SwiftToCxx/module/module-to-namespace.swift +++ b/test/Interop/SwiftToCxx/module/module-to-namespace.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -module-name Test -emit-cxx-header-path %t/empty.h +// RUN: %target-swift-frontend %s -typecheck -module-name Test -enable-cxx-interop -emit-clang-header-path %t/empty.h // RUN: %FileCheck %s < %t/empty.h // RUN: %check-interop-cxx-header-in-clang(%t/empty.h) diff --git a/test/PrintAsCxx/empty.swift b/test/PrintAsCxx/empty.swift index 0b3c6c64c9b07..317d3a8416f12 100644 --- a/test/PrintAsCxx/empty.swift +++ b/test/PrintAsCxx/empty.swift @@ -1,13 +1,7 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) %s -typecheck -emit-cxx-header-path %t/empty.h +// RUN: %target-swift-frontend %s -typecheck -enable-cxx-interop -emit-clang-header-path %t/empty.h // RUN: %FileCheck %s < %t/empty.h -// RUN: %check-cxx-header-in-clang -std=c++14 %t/empty.h -// RUN: %check-cxx-header-in-clang -std=c++17 %t/empty.h - -// CHECK-NOT: @import Swift; -// CHECK-NOT: IBSegueAction - // CHECK-LABEL: #ifndef EMPTY_SWIFT_H // CHECK-NEXT: #define EMPTY_SWIFT_H @@ -63,7 +57,19 @@ // CHECK: # define SWIFT_EXTENSION(M) // CHECK: # define OBJC_DESIGNATED_INITIALIZER -// CHECK-LABEL: namespace empty { -// CHECK: } // namespace empty +// CHECK-LABEL: #if defined(__OBJC__) +// CHECK-NEXT: #if !defined(IBSegueAction) +// CHECK-NEXT: # define IBSegueAction +// CHECK-NEXT: #endif + +// CHECK-LABEL: #if defined(__OBJC__) +// CHECK-NEXT: #if __has_feature(modules) + +// CHECK-LABEL: #if defined(__OBJC__) +// CHECK-NEXT: #endif +// CHECK-NEXT: #if defined(__cplusplus) +// CHECK-NEXT: namespace empty { +// CHECK: } // namespace empty +// CHECK: #endif // CHECK-NOT: @ diff --git a/test/PrintAsObjC/Inputs/comments-expected-output.h b/test/PrintAsObjC/Inputs/comments-expected-output.h index eccfc9b978141..2a23d5ed3d579 100644 --- a/test/PrintAsObjC/Inputs/comments-expected-output.h +++ b/test/PrintAsObjC/Inputs/comments-expected-output.h @@ -424,6 +424,9 @@ SWIFT_CLASS("_TtC8comments13UnorderedList") - (void)f0; @end +#endif +#if defined(__cplusplus) +#endif #if __has_attribute(external_source_symbol) # pragma clang attribute pop #endif From ee471f3672a37b814e61d27d7993f140cb96c63e Mon Sep 17 00:00:00 2001 From: lee Date: Sun, 20 Mar 2022 09:47:44 +0700 Subject: [PATCH 228/242] Fixed minor typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcc01bb173ec0..cb004b5c04831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,7 +96,7 @@ For example, Swift 5.6 would allow the following code, which at runtime would co * [SE-0341][]: - Opaque types can now be used in the parameters of functions and subscripts, wher they provide a shorthand syntax for the introduction of a generic parameter. For example, the following: + Opaque types can now be used in the parameters of functions and subscripts, when they provide a shorthand syntax for the introduction of a generic parameter. For example, the following: ```swift func horizontal(_ v1: some View, _ v2: some View) -> some View { From 80bc75ab9b973758b8be78c24eac2a3397f1c3dd Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Sun, 20 Mar 2022 20:22:02 -0700 Subject: [PATCH 229/242] [cxx-interop] Emit C++ declarations only when '-clang-header-expose-public-decl' is enabled This fix also ensures that we only emit C++ functions for now --- include/swift/Frontend/FrontendOptions.h | 4 ++++ include/swift/Option/FrontendOptions.td | 5 +++++ include/swift/PrintAsClang/PrintAsClang.h | 9 +++++---- lib/Frontend/ArgsToFrontendOptionsConverter.cpp | 2 ++ lib/FrontendTool/FrontendTool.cpp | 15 +++++++++------ lib/PrintAsClang/ModuleContentsWriter.cpp | 10 ++++++++-- lib/PrintAsClang/PrintAsClang.cpp | 5 +++-- .../SwiftToCxx/functions/cdecl-execution.cpp | 2 +- test/Interop/SwiftToCxx/functions/cdecl.swift | 2 +- .../functions/function-availability.swift | 2 +- .../functions/swift-functions-execution.cpp | 2 +- .../SwiftToCxx/functions/swift-functions.swift | 2 +- .../SwiftToCxx/module/module-to-namespace.swift | 2 +- test/PrintAsCxx/empty.swift | 2 +- 14 files changed, 43 insertions(+), 21 deletions(-) diff --git a/include/swift/Frontend/FrontendOptions.h b/include/swift/Frontend/FrontendOptions.h index 9248cc4700b6a..340c3269e06cb 100644 --- a/include/swift/Frontend/FrontendOptions.h +++ b/include/swift/Frontend/FrontendOptions.h @@ -378,6 +378,10 @@ class FrontendOptions { /// '.../lib/swift', otherwise '.../lib/swift_static'. bool UseSharedResourceFolder = true; + /// Indicates whether to expose all public declarations in the generated clang + /// header. + bool ExposePublicDeclsInClangHeader = false; + /// \return true if the given action only parses without doing other compilation steps. static bool shouldActionOnlyParse(ActionType); diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 3edff91a9aa89..4f1f9d2fef01d 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -1031,4 +1031,9 @@ def skip_import_in_public_interface: HelpText<"Skip the import statement corresponding to a module name " "when printing the public interface.">; +def clang_header_expose_public_decls: + Flag<["-"], "clang-header-expose-public-decls">, + HelpText<"Expose all public declarations in the generated clang header">, + Flags<[FrontendOption, HelpHidden]>; + } // end let Flags = [FrontendOption, NoDriverOption, HelpHidden] diff --git a/include/swift/PrintAsClang/PrintAsClang.h b/include/swift/PrintAsClang/PrintAsClang.h index a52699b330dc4..b7077a0828d7d 100644 --- a/include/swift/PrintAsClang/PrintAsClang.h +++ b/include/swift/PrintAsClang/PrintAsClang.h @@ -26,13 +26,14 @@ namespace swift { /// The Objective-C compatible declarations are printed into a block that /// ensures that those declarations are only usable when the header is /// compiled in Objective-C mode. - /// The C++ compatible declarations are printed into a block that ensures - /// that those declarations are only usable when the header is compiled in - /// C++ mode. + /// The C++ compatible declarations are printed into a block that ensures + /// that those declarations are only usable when the header is compiled in + /// C++ mode. /// /// Returns true on error. bool printAsClangHeader(raw_ostream &out, ModuleDecl *M, - StringRef bridgingHeader); + StringRef bridgingHeader, + bool ExposePublicDeclsInClangHeader); } #endif diff --git a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp index 02071f42c0c8d..db6f96ebaa66c 100644 --- a/lib/Frontend/ArgsToFrontendOptionsConverter.cpp +++ b/lib/Frontend/ArgsToFrontendOptionsConverter.cpp @@ -274,6 +274,8 @@ bool ArgsToFrontendOptionsConverter::convert( Opts.EnableIncrementalDependencyVerifier |= Args.hasArg(OPT_verify_incremental_dependencies); Opts.UseSharedResourceFolder = !Args.hasArg(OPT_use_static_resource_dir); Opts.DisableBuildingInterface = Args.hasArg(OPT_disable_building_interface); + Opts.ExposePublicDeclsInClangHeader = + Args.hasArg(OPT_clang_header_expose_public_decls); computeImportObjCHeaderOptions(); computeImplicitImportModuleNames(OPT_import_module, /*isTestable=*/false); diff --git a/lib/FrontendTool/FrontendTool.cpp b/lib/FrontendTool/FrontendTool.cpp index 9317a20864f24..8da6eb7f2a954 100644 --- a/lib/FrontendTool/FrontendTool.cpp +++ b/lib/FrontendTool/FrontendTool.cpp @@ -179,13 +179,15 @@ static bool writeSIL(SILModule &SM, const PrimarySpecificPaths &PSPs, /// /// \see swift::printAsClangHeader static bool printAsClangHeaderIfNeeded(StringRef outputPath, ModuleDecl *M, - StringRef bridgingHeader) { + StringRef bridgingHeader, + bool ExposePublicDeclsInClangHeader) { if (outputPath.empty()) return false; - return withOutputFile(M->getDiags(), outputPath, - [&](raw_ostream &out) -> bool { - return printAsClangHeader(out, M, bridgingHeader); - }); + return withOutputFile( + M->getDiags(), outputPath, [&](raw_ostream &out) -> bool { + return printAsClangHeader(out, M, bridgingHeader, + ExposePublicDeclsInClangHeader); + }); } /// Prints the stable module interface for \p M to \p outputPath. @@ -826,7 +828,8 @@ static bool emitAnyWholeModulePostTypeCheckSupplementaryOutputs( } hadAnyError |= printAsClangHeaderIfNeeded( Invocation.getClangHeaderOutputPathForAtMostOnePrimary(), - Instance.getMainModule(), BridgingHeaderPathForPrint); + Instance.getMainModule(), BridgingHeaderPathForPrint, + opts.ExposePublicDeclsInClangHeader); } // Only want the header if there's been any errors, ie. there's not much diff --git a/lib/PrintAsClang/ModuleContentsWriter.cpp b/lib/PrintAsClang/ModuleContentsWriter.cpp index e7d2caaa4df37..682a712cbcb1f 100644 --- a/lib/PrintAsClang/ModuleContentsWriter.cpp +++ b/lib/PrintAsClang/ModuleContentsWriter.cpp @@ -124,13 +124,15 @@ class ModuleWriter { std::vector declsToWrite; DelayedMemberSet delayedMembers; DeclAndTypePrinter printer; + OutputLanguageMode outputLangMode; public: ModuleWriter(raw_ostream &os, raw_ostream &prologueOS, llvm::SmallPtrSetImpl &imports, ModuleDecl &mod, AccessLevel access, OutputLanguageMode outputLang) : os(os), imports(imports), M(mod), - printer(M, os, prologueOS, delayedMembers, access, outputLang) {} + printer(M, os, prologueOS, delayedMembers, access, outputLang), + outputLangMode(outputLang) {} /// Returns true if we added the decl's module to the import set, false if /// the decl is a local decl. @@ -576,7 +578,11 @@ class ModuleWriter { const Decl *D = declsToWrite.back(); bool success = true; - if (isa(D)) { + if (outputLangMode == OutputLanguageMode::Cxx) { + if (auto FD = dyn_cast(D)) + success = writeFunc(FD); + // FIXME: Warn on unsupported exported decl. + } else if (isa(D)) { if (auto CD = dyn_cast(D)) success = writeClass(CD); else if (auto PD = dyn_cast(D)) diff --git a/lib/PrintAsClang/PrintAsClang.cpp b/lib/PrintAsClang/PrintAsClang.cpp index 116067978d1e1..c9961870a9acf 100644 --- a/lib/PrintAsClang/PrintAsClang.cpp +++ b/lib/PrintAsClang/PrintAsClang.cpp @@ -458,7 +458,8 @@ static std::string getModuleContentsCxxString(ModuleDecl &M) { } bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M, - StringRef bridgingHeader) { + StringRef bridgingHeader, + bool ExposePublicDeclsInClangHeader) { llvm::PrettyStackTraceString trace("While generating Clang header"); SmallPtrSet imports; @@ -472,7 +473,7 @@ bool swift::printAsClangHeader(raw_ostream &os, ModuleDecl *M, emitObjCConditional(os, [&] { os << objcModuleContents.str(); }); emitCxxConditional(os, [&] { // FIXME: Expose Swift with @expose by default. - if (M->getASTContext().LangOpts.EnableCXXInterop) { + if (ExposePublicDeclsInClangHeader) { os << getModuleContentsCxxString(*M); } }); diff --git a/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp b/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp index ecef6bad7f3a9..0e1719fc52f84 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp +++ b/test/Interop/SwiftToCxx/functions/cdecl-execution.cpp @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %S/cdecl.swift -typecheck -module-name CdeclFunctions -enable-cxx-interop -emit-clang-header-path %t/functions.h +// RUN: %target-swift-frontend %S/cdecl.swift -typecheck -module-name CdeclFunctions -clang-header-expose-public-decls -emit-clang-header-path %t/functions.h // RUN: %target-interop-build-clangxx -c %s -I %t -o %t/cdecl-execution.o // RUN: %target-interop-build-swift %S/cdecl.swift -o %t/cdecl-execution -Xlinker %t/cdecl-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain diff --git a/test/Interop/SwiftToCxx/functions/cdecl.swift b/test/Interop/SwiftToCxx/functions/cdecl.swift index 94055be7bee29..755165fffd7a7 100644 --- a/test/Interop/SwiftToCxx/functions/cdecl.swift +++ b/test/Interop/SwiftToCxx/functions/cdecl.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name CdeclFunctions -enable-cxx-interop -emit-clang-header-path %t/cdecl.h +// RUN: %target-swift-frontend %s -typecheck -module-name CdeclFunctions -clang-header-expose-public-decls -emit-clang-header-path %t/cdecl.h // RUN: %FileCheck %s < %t/cdecl.h // RUN: %check-interop-cxx-header-in-clang(%t/cdecl.h) diff --git a/test/Interop/SwiftToCxx/functions/function-availability.swift b/test/Interop/SwiftToCxx/functions/function-availability.swift index b3d9f7ca116fb..c026f6d1f374f 100644 --- a/test/Interop/SwiftToCxx/functions/function-availability.swift +++ b/test/Interop/SwiftToCxx/functions/function-availability.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h +// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-public-decls -emit-clang-header-path %t/functions.h // RUN: %FileCheck %s < %t/functions.h // RUN: %check-interop-cxx-header-in-clang(%t/functions.h) diff --git a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp index e9c834d7b87eb..06d40a05b830b 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp +++ b/test/Interop/SwiftToCxx/functions/swift-functions-execution.cpp @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %S/swift-functions.swift -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h +// RUN: %target-swift-frontend %S/swift-functions.swift -typecheck -module-name Functions -clang-header-expose-public-decls -emit-clang-header-path %t/functions.h // RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-functions-execution.o // RUN: %target-interop-build-swift %S/swift-functions.swift -o %t/swift-functions-execution -Xlinker %t/swift-functions-execution.o -module-name Functions -Xfrontend -entry-point-function-name -Xfrontend swiftMain diff --git a/test/Interop/SwiftToCxx/functions/swift-functions.swift b/test/Interop/SwiftToCxx/functions/swift-functions.swift index 6b889d20959c0..22d71bb44af67 100644 --- a/test/Interop/SwiftToCxx/functions/swift-functions.swift +++ b/test/Interop/SwiftToCxx/functions/swift-functions.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Functions -enable-cxx-interop -emit-clang-header-path %t/functions.h +// RUN: %target-swift-frontend %s -typecheck -module-name Functions -clang-header-expose-public-decls -emit-clang-header-path %t/functions.h // RUN: %FileCheck %s < %t/functions.h // RUN: %check-interop-cxx-header-in-clang(%t/functions.h) diff --git a/test/Interop/SwiftToCxx/module/module-to-namespace.swift b/test/Interop/SwiftToCxx/module/module-to-namespace.swift index db53e90cd3167..d9ff74fd5c1e7 100644 --- a/test/Interop/SwiftToCxx/module/module-to-namespace.swift +++ b/test/Interop/SwiftToCxx/module/module-to-namespace.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -module-name Test -enable-cxx-interop -emit-clang-header-path %t/empty.h +// RUN: %target-swift-frontend %s -typecheck -module-name Test -clang-header-expose-public-decls -emit-clang-header-path %t/empty.h // RUN: %FileCheck %s < %t/empty.h // RUN: %check-interop-cxx-header-in-clang(%t/empty.h) diff --git a/test/PrintAsCxx/empty.swift b/test/PrintAsCxx/empty.swift index 317d3a8416f12..2d45d28fb871c 100644 --- a/test/PrintAsCxx/empty.swift +++ b/test/PrintAsCxx/empty.swift @@ -1,5 +1,5 @@ // RUN: %empty-directory(%t) -// RUN: %target-swift-frontend %s -typecheck -enable-cxx-interop -emit-clang-header-path %t/empty.h +// RUN: %target-swift-frontend %s -typecheck -clang-header-expose-public-decls -emit-clang-header-path %t/empty.h // RUN: %FileCheck %s < %t/empty.h // CHECK-LABEL: #ifndef EMPTY_SWIFT_H From 67bb76a34173af1cb99b25676b6b1a683d5d0b37 Mon Sep 17 00:00:00 2001 From: Federico Zanetello Date: Mon, 21 Mar 2022 11:36:51 +0700 Subject: [PATCH 230/242] fix change log typo --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb004b5c04831..5ae2e3983d2ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,7 @@ func talkTo(greeter: Greeter) async throws { * The compiler now emits a warning when a non-final class conforms to a protocol that imposes a same-type requirement between `Self` and an associated type. This is because such a requirement makes the conformance unsound for subclasses. -For example, Swift 5.6 would allow the following code, which at runtime would construct an instanec of `C` and not `SubC` as expected: +For example, Swift 5.6 would allow the following code, which at runtime would construct an instance of `C` and not `SubC` as expected: ```swift protocol P { From e2a62f1a6044d56c9b360639e95af38be253ab56 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Fri, 18 Mar 2022 12:18:17 +0100 Subject: [PATCH 231/242] [CodeCompletion] Migrate expression completions to solver-based --- include/swift/IDE/CompletionLookup.h | 15 +++ include/swift/IDE/ExprCompletion.h | 66 +++++++++++++ include/swift/Sema/ConstraintSystem.h | 47 ++++++++++ lib/IDE/ArgumentCompletion.cpp | 4 +- lib/IDE/CMakeLists.txt | 1 + lib/IDE/CodeCompletion.cpp | 23 ++++- lib/IDE/CompletionLookup.cpp | 7 +- lib/IDE/ExprCompletion.cpp | 92 +++++++++++++++++++ lib/Sema/CSApply.cpp | 3 + lib/Sema/CSGen.cpp | 1 + lib/Sema/CSSimplify.cpp | 3 + lib/Sema/CompletionContextFinder.cpp | 2 +- lib/Sema/TypeCheckConstraints.cpp | 1 + test/IDE/complete_in_closures.swift | 2 +- .../0031-fallback-typecheck-call-arg.swift | 9 ++ ...ion-application-target-applied-twice.swift | 17 ++++ ...n-application-target-applied-twice-2.swift | 20 ++++ 17 files changed, 308 insertions(+), 5 deletions(-) create mode 100644 include/swift/IDE/ExprCompletion.h create mode 100644 lib/IDE/ExprCompletion.cpp create mode 100644 validation-test/IDE/crashers_2_fixed/0031-fallback-typecheck-call-arg.swift create mode 100644 validation-test/IDE/crashers_2_fixed/0034-closure-solution-application-target-applied-twice.swift create mode 100644 validation-test/IDE/crashers_2_fixed/0035-closure-solution-application-target-applied-twice-2.swift diff --git a/include/swift/IDE/CompletionLookup.h b/include/swift/IDE/CompletionLookup.h index 425841e385e2d..e7b118c3f0ff4 100644 --- a/include/swift/IDE/CompletionLookup.h +++ b/include/swift/IDE/CompletionLookup.h @@ -113,6 +113,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { /// Expected types of the code completion expression. ExpectedTypeContext expectedTypeContext; + /// Variables whose type was determined while type checking the code + /// completion expression and that are thus not recorded in the AST. + /// This in particular applies to params of closures that contain the code + /// completion token. + llvm::SmallDenseMap SolutionSpecificVarTypes; + bool CanCurrDeclContextHandleAsync = false; bool HaveDot = false; bool IsUnwrappedOptional = false; @@ -206,6 +212,11 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { const DeclContext *CurrDeclContext, CodeCompletionContext *CompletionContext = nullptr); + void setSolutionSpecificVarTypes( + llvm::SmallDenseMap SolutionSpecificVarTypes) { + this->SolutionSpecificVarTypes = SolutionSpecificVarTypes; + } + void setHaveDot(SourceLoc DotLoc) { HaveDot = true; this->DotLoc = DotLoc; @@ -226,6 +237,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer { void setIdealExpectedType(Type Ty) { expectedTypeContext.setIdealType(Ty); } + void setCanCurrDeclContextHandleAsync(bool CanCurrDeclContextHandleAsync) { + this->CanCurrDeclContextHandleAsync = CanCurrDeclContextHandleAsync; + } + const ExpectedTypeContext *getExpectedTypeContext() const { return &expectedTypeContext; } diff --git a/include/swift/IDE/ExprCompletion.h b/include/swift/IDE/ExprCompletion.h new file mode 100644 index 0000000000000..4b60b8d8e6e4b --- /dev/null +++ b/include/swift/IDE/ExprCompletion.h @@ -0,0 +1,66 @@ +//===--- ExprCompletion.h -------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_IDE_EXPRCOMPLETION_H +#define SWIFT_IDE_EXPRCOMPLETION_H + +#include "swift/IDE/CodeCompletionConsumer.h" +#include "swift/IDE/CodeCompletionContext.h" +#include "swift/Sema/CodeCompletionTypeChecking.h" + +namespace swift { +namespace ide { + +class ExprTypeCheckCompletionCallback : public TypeCheckCompletionCallback { +public: + struct Result { + /// The contextual type that the code completion expression should produce. + Type ExpectedType; + + /// If the code completion expression is an implicit return in a + /// single-expression closure. + bool IsImplicitSingleExpressionReturn; + + /// Whether the surrounding context is async and thus calling async + /// functions is supported. + bool IsInAsyncContext; + + /// Types of variables that were determined in the solution that produced + /// this result. This in particular includes parameters of closures that + /// were type-checked with the code completion expression. + llvm::SmallDenseMap SolutionSpecificVarTypes; + }; + +private: + CodeCompletionExpr *CompletionExpr; + DeclContext *DC; + + SmallVector Results; + +public: + /// \param DC The decl context in which the \p CompletionExpr occurs. + ExprTypeCheckCompletionCallback(CodeCompletionExpr *CompletionExpr, + DeclContext *DC) + : CompletionExpr(CompletionExpr), DC(DC) {} + + void sawSolution(const constraints::Solution &solution) override; + + /// \param CCLoc The location of the code completion token. + void deliverResults(SourceLoc CCLoc, + ide::CodeCompletionContext &CompletionCtx, + CodeCompletionConsumer &Consumer); +}; + +} // end namespace ide +} // end namespace swift + +#endif // SWIFT_IDE_EXPRCOMPLETION_H diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 581595f12e7e4..9e49d6db8a495 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -978,6 +978,7 @@ class SolutionApplicationTargetsKey { tombstone, stmtCondElement, expr, + closure, stmt, pattern, patternBindingEntry, @@ -1020,6 +1021,11 @@ class SolutionApplicationTargetsKey { storage.expr = expr; } + SolutionApplicationTargetsKey(const ClosureExpr *closure) { + kind = Kind::closure; + storage.expr = closure; + } + SolutionApplicationTargetsKey(const Stmt *stmt) { kind = Kind::stmt; storage.stmt = stmt; @@ -1056,6 +1062,7 @@ class SolutionApplicationTargetsKey { return lhs.storage.stmtCondElement == rhs.storage.stmtCondElement; case Kind::expr: + case Kind::closure: return lhs.storage.expr == rhs.storage.expr; case Kind::stmt: @@ -1096,6 +1103,7 @@ class SolutionApplicationTargetsKey { DenseMapInfo::getHashValue(storage.stmtCondElement)); case Kind::expr: + case Kind::closure: return hash_combine( DenseMapInfo::getHashValue(static_cast(kind)), DenseMapInfo::getHashValue(storage.expr)); @@ -1623,6 +1631,7 @@ class SolutionApplicationTarget { public: enum class Kind { expression, + closure, function, stmtCondition, caseLabelItem, @@ -1684,6 +1693,14 @@ class SolutionApplicationTarget { }; } expression; + struct { + /// The closure expression being type-checked. + ClosureExpr *closure; + + /// The type to which the expression should be converted. + Type convertType; + } closure; + struct { AnyFunctionRef function; BraceStmt *body; @@ -1735,6 +1752,12 @@ class SolutionApplicationTarget { setPattern(pattern); } + SolutionApplicationTarget(ClosureExpr *closure, Type convertType) { + kind = Kind::closure; + this->closure.closure = closure; + this->closure.convertType = convertType; + } + SolutionApplicationTarget(AnyFunctionRef fn) : SolutionApplicationTarget(fn, fn.getBody()) { } @@ -1831,6 +1854,7 @@ class SolutionApplicationTarget { case Kind::expression: return expression.expression; + case Kind::closure: case Kind::function: case Kind::stmtCondition: case Kind::caseLabelItem: @@ -1846,6 +1870,9 @@ class SolutionApplicationTarget { case Kind::expression: return expression.dc; + case Kind::closure: + return closure.closure; + case Kind::function: return function.function.getAsDeclContext(); @@ -1934,6 +1961,11 @@ class SolutionApplicationTarget { return cast(expression.pattern); } + Type getClosureContextualType() const { + assert(kind == Kind::closure); + return closure.convertType; + } + /// For a pattern initialization target, retrieve the contextual pattern. ContextualPattern getContextualPattern() const; @@ -2062,6 +2094,7 @@ class SolutionApplicationTarget { Optional getAsFunction() const { switch (kind) { case Kind::expression: + case Kind::closure: case Kind::stmtCondition: case Kind::caseLabelItem: case Kind::patternBinding: @@ -2077,6 +2110,7 @@ class SolutionApplicationTarget { Optional getAsStmtCondition() const { switch (kind) { case Kind::expression: + case Kind::closure: case Kind::function: case Kind::caseLabelItem: case Kind::patternBinding: @@ -2092,6 +2126,7 @@ class SolutionApplicationTarget { Optional getAsCaseLabelItem() const { switch (kind) { case Kind::expression: + case Kind::closure: case Kind::function: case Kind::stmtCondition: case Kind::patternBinding: @@ -2107,6 +2142,7 @@ class SolutionApplicationTarget { PatternBindingDecl *getAsPatternBinding() const { switch (kind) { case Kind::expression: + case Kind::closure: case Kind::function: case Kind::stmtCondition: case Kind::caseLabelItem: @@ -2122,6 +2158,7 @@ class SolutionApplicationTarget { VarDecl *getAsUninitializedWrappedVar() const { switch (kind) { case Kind::expression: + case Kind::closure: case Kind::function: case Kind::stmtCondition: case Kind::caseLabelItem: @@ -2137,6 +2174,7 @@ class SolutionApplicationTarget { Pattern *getAsUninitializedVar() const { switch (kind) { case Kind::expression: + case Kind::closure: case Kind::function: case Kind::stmtCondition: case Kind::caseLabelItem: @@ -2152,6 +2190,7 @@ class SolutionApplicationTarget { Type getTypeOfUninitializedVar() const { switch (kind) { case Kind::expression: + case Kind::closure: case Kind::function: case Kind::stmtCondition: case Kind::caseLabelItem: @@ -2167,6 +2206,7 @@ class SolutionApplicationTarget { PatternBindingDecl *getPatternBindingOfUninitializedVar() const { switch (kind) { case Kind::expression: + case Kind::closure: case Kind::function: case Kind::stmtCondition: case Kind::caseLabelItem: @@ -2182,6 +2222,7 @@ class SolutionApplicationTarget { unsigned getIndexOfUninitializedVar() const { switch (kind) { case Kind::expression: + case Kind::closure: case Kind::function: case Kind::stmtCondition: case Kind::caseLabelItem: @@ -2210,6 +2251,9 @@ class SolutionApplicationTarget { case Kind::expression: return expression.expression->getSourceRange(); + case Kind::closure: + return closure.closure->getSourceRange(); + case Kind::function: return function.body->getSourceRange(); @@ -2240,6 +2284,9 @@ class SolutionApplicationTarget { case Kind::expression: return expression.expression->getLoc(); + case Kind::closure: + return closure.closure->getLoc(); + case Kind::function: return function.function.getLoc(); diff --git a/lib/IDE/ArgumentCompletion.cpp b/lib/IDE/ArgumentCompletion.cpp index d019522ddfefe..acc2cfdb3d6da 100644 --- a/lib/IDE/ArgumentCompletion.cpp +++ b/lib/IDE/ArgumentCompletion.cpp @@ -106,7 +106,9 @@ void ArgumentTypeCheckCompletionCallback::sawSolution(const Solution &S) { } if (!ParentCall || ParentCall == CompletionExpr) { - assert(false && "no containing call?"); + // We might not have a call that contains the code completion expression if + // we type-checked the fallback code completion expression that only + // contains the code completion token, but not the surrounding call. return; } diff --git a/lib/IDE/CMakeLists.txt b/lib/IDE/CMakeLists.txt index 83d79f787d236..02301eca46810 100644 --- a/lib/IDE/CMakeLists.txt +++ b/lib/IDE/CMakeLists.txt @@ -20,6 +20,7 @@ add_swift_host_library(swiftIDE STATIC ConformingMethodList.cpp DependencyChecking.cpp DotExprCompletion.cpp + ExprCompletion.cpp ExprContextAnalysis.cpp Formatting.cpp FuzzyStringMatcher.cpp diff --git a/lib/IDE/CodeCompletion.cpp b/lib/IDE/CodeCompletion.cpp index 52e99ebc8db40..98afd4be11744 100644 --- a/lib/IDE/CodeCompletion.cpp +++ b/lib/IDE/CodeCompletion.cpp @@ -40,6 +40,7 @@ #include "swift/IDE/CompletionLookup.h" #include "swift/IDE/CompletionOverrideLookup.h" #include "swift/IDE/DotExprCompletion.h" +#include "swift/IDE/ExprCompletion.h" #include "swift/IDE/KeyPathCompletion.h" #include "swift/IDE/UnresolvedMemberCompletion.h" #include "swift/IDE/Utils.h" @@ -1392,6 +1393,26 @@ bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { CurDeclContext, CompletionContext, Consumer); return true; } + case CompletionKind::StmtOrExpr: { + assert(CodeCompleteTokenExpr); + assert(CurDeclContext); + + ExprTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr, + CurDeclContext); + llvm::SaveAndRestore CompletionCollector( + Context.CompletionCallback, &Lookup); + typeCheckContextAt(CurDeclContext, CompletionLoc); + + if (!Lookup.gotCallback()) { + Lookup.fallbackTypeCheck(CurDeclContext); + } + + addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); + + SourceLoc CCLoc = P.Context.SourceMgr.getCodeCompletionLoc(); + Lookup.deliverResults(CCLoc, CompletionContext, Consumer); + return true; + } default: return false; } @@ -1514,10 +1535,10 @@ void CodeCompletionCallbacksImpl::doneParsing() { case CompletionKind::UnresolvedMember: case CompletionKind::KeyPathExprSwift: case CompletionKind::CallArg: + case CompletionKind::StmtOrExpr: llvm_unreachable("should be already handled"); return; - case CompletionKind::StmtOrExpr: case CompletionKind::ForEachSequence: case CompletionKind::PostfixExprBeginning: { ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); diff --git a/lib/IDE/CompletionLookup.cpp b/lib/IDE/CompletionLookup.cpp index c4cbfcf3f675b..76019e55dc694 100644 --- a/lib/IDE/CompletionLookup.cpp +++ b/lib/IDE/CompletionLookup.cpp @@ -804,8 +804,13 @@ void CompletionLookup::addVarDeclRef(const VarDecl *VD, assert(!Name.empty() && "name should not be empty"); Type VarType; - if (VD->hasInterfaceType()) + auto SolutionSpecificType = SolutionSpecificVarTypes.find(VD); + if (SolutionSpecificType != SolutionSpecificVarTypes.end()) { + assert(!VarType && "Type recorded in the AST and is also solution-specific?"); + VarType = SolutionSpecificType->second; + } else if (VD->hasInterfaceType()) { VarType = getTypeOfMember(VD, dynamicLookupInfo); + } Optional NotRecommended; // "not recommended" in its own getter. diff --git a/lib/IDE/ExprCompletion.cpp b/lib/IDE/ExprCompletion.cpp new file mode 100644 index 0000000000000..c97918a6fbe82 --- /dev/null +++ b/lib/IDE/ExprCompletion.cpp @@ -0,0 +1,92 @@ +//===--- ExprCompletion.cpp -----------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "swift/IDE/ExprCompletion.h" +#include "swift/IDE/CodeCompletion.h" +#include "swift/IDE/CompletionLookup.h" +#include "swift/Sema/ConstraintSystem.h" + +using namespace swift; +using namespace swift::ide; +using namespace swift::constraints; + +void ExprTypeCheckCompletionCallback::sawSolution( + const constraints::Solution &S) { + TypeCheckCompletionCallback::sawSolution(S); + + auto &CS = S.getConstraintSystem(); + + // Prefer to get the expected type as the completion expression's contextual + // type. If that fails (because there is no explicit contextual type spelled + // out in the source), the code completion expression will have been + // type-checked to its expected contextual type. + Type ExpectedTy = + CS.getContextualType(CompletionExpr, /*forConstraint=*/false); + if (!ExpectedTy) { + ExpectedTy = S.getResolvedType(CompletionExpr); + } + if (ExpectedTy->hasUnresolvedType()) { + ExpectedTy = Type(); + } + + bool ImplicitReturn = isImplicitSingleExpressionReturn(CS, CompletionExpr); + + // We are in an async context if + // - the decl context is async or + // - the decl context is sync but it's used in a context that expectes an + // async function. This happens if the code completion token is in a + // closure that doesn't contain any async calles. Thus the closure is + // type-checked as non-async, but it might get converted to an async + // closure based on its contextual type. + bool isAsync = CS.isAsynchronousContext(DC); + if (!isAsync) { + auto target = S.solutionApplicationTargets.find(dyn_cast(DC)); + if (target != S.solutionApplicationTargets.end()) { + if (auto ContextTy = target->second.getClosureContextualType()) { + if (auto ContextFuncTy = + S.simplifyType(ContextTy)->getAs()) { + isAsync = ContextFuncTy->isAsync(); + } + } + } + } + + llvm::SmallDenseMap SolutionSpecificVarTypes; + for (auto NT : S.nodeTypes) { + if (auto VD = dyn_cast_or_null(NT.first.dyn_cast())) { + SolutionSpecificVarTypes[VD] = S.simplifyType(NT.second); + } + } + + Results.push_back( + {ExpectedTy, ImplicitReturn, isAsync, SolutionSpecificVarTypes}); +} + +void ExprTypeCheckCompletionCallback::deliverResults( + SourceLoc CCLoc, ide::CodeCompletionContext &CompletionCtx, + CodeCompletionConsumer &Consumer) { + ASTContext &Ctx = DC->getASTContext(); + CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC, + &CompletionCtx); + + for (auto &Result : Results) { + Lookup.setExpectedTypes(Result.ExpectedType, + Result.IsImplicitSingleExpressionReturn); + Lookup.setCanCurrDeclContextHandleAsync(Result.IsInAsyncContext); + Lookup.setSolutionSpecificVarTypes(Result.SolutionSpecificVarTypes); + + Lookup.getValueCompletionsInDeclContext(CCLoc); + Lookup.getSelfTypeCompletionInDeclContext(CCLoc, /*isForDeclResult=*/false); + } + + deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer); +} diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 47248f59d8303..ba01122ff84d4 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -9017,6 +9017,9 @@ SolutionApplicationTarget SolutionApplicationTarget::walk(ASTWalker &walker) { return result; } + case Kind::closure: + return *this; + case Kind::function: return SolutionApplicationTarget( *getAsFunction(), diff --git a/lib/Sema/CSGen.cpp b/lib/Sema/CSGen.cpp index fb95be286feb3..05fb8ca7c3bbe 100644 --- a/lib/Sema/CSGen.cpp +++ b/lib/Sema/CSGen.cpp @@ -4102,6 +4102,7 @@ bool ConstraintSystem::generateConstraints( case SolutionApplicationTarget::Kind::expression: llvm_unreachable("Handled above"); + case SolutionApplicationTarget::Kind::closure: case SolutionApplicationTarget::Kind::caseLabelItem: case SolutionApplicationTarget::Kind::function: case SolutionApplicationTarget::Kind::stmtCondition: diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index c7b1018c6c4df..4f9c5e1ad369a 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -9917,6 +9917,9 @@ bool ConstraintSystem::resolveClosure(TypeVariableType *typeVar, } } + SolutionApplicationTarget target(closure, contextualType); + setSolutionApplicationTarget(closure, target); + // Generate constraints from the body of this closure. return !generateConstraints(closure); } diff --git a/lib/Sema/CompletionContextFinder.cpp b/lib/Sema/CompletionContextFinder.cpp index 3afae866dcfdf..2b27972f7a393 100644 --- a/lib/Sema/CompletionContextFinder.cpp +++ b/lib/Sema/CompletionContextFinder.cpp @@ -125,7 +125,7 @@ Optional CompletionContextFinder::getFallbackCompletionExpr() const { if (fallback) return fallback; - if (getCompletionExpr()->getBase() && getCompletionExpr() != InitialExpr) + if (getCompletionExpr() != InitialExpr) return Fallback{getCompletionExpr(), fallbackDC, separatePrecheck}; return None; } diff --git a/lib/Sema/TypeCheckConstraints.cpp b/lib/Sema/TypeCheckConstraints.cpp index ca87285584fdf..ce3a407fb5d1e 100644 --- a/lib/Sema/TypeCheckConstraints.cpp +++ b/lib/Sema/TypeCheckConstraints.cpp @@ -313,6 +313,7 @@ void constraints::performSyntacticDiagnosticsForTarget( target.getFunctionBody()->walk(walker); return; } + case SolutionApplicationTarget::Kind::closure: case SolutionApplicationTarget::Kind::stmtCondition: case SolutionApplicationTarget::Kind::caseLabelItem: case SolutionApplicationTarget::Kind::patternBinding: diff --git a/test/IDE/complete_in_closures.swift b/test/IDE/complete_in_closures.swift index 3a3e914af8afd..32b54bee458f5 100644 --- a/test/IDE/complete_in_closures.swift +++ b/test/IDE/complete_in_closures.swift @@ -104,7 +104,7 @@ struct NestedStructWithClosureMember1 { struct StructWithClosureMemberAndLocal { var c = { var x = 0 - #^DELAYED_10?check=WITH_GLOBAL_DECLS_AND_LOCAL1^# + #^DELAYED_10?check=WITH_GLOBAL_DECLS_AND_LOCAL1;xfail=sr16012^# } } diff --git a/validation-test/IDE/crashers_2_fixed/0031-fallback-typecheck-call-arg.swift b/validation-test/IDE/crashers_2_fixed/0031-fallback-typecheck-call-arg.swift new file mode 100644 index 0000000000000..126aaf64d78e8 --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0031-fallback-typecheck-call-arg.swift @@ -0,0 +1,9 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +func tryMap(_ x: (String) -> Void) {} + +func fetch() { + tryMap { data in + doesNotExist(data: data, #^COMPLETE^#) + } +} diff --git a/validation-test/IDE/crashers_2_fixed/0034-closure-solution-application-target-applied-twice.swift b/validation-test/IDE/crashers_2_fixed/0034-closure-solution-application-target-applied-twice.swift new file mode 100644 index 0000000000000..e1a2fd855dd60 --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0034-closure-solution-application-target-applied-twice.swift @@ -0,0 +1,17 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +@resultBuilder public struct WiewBuilder { + static func buildBlock(_ content: T) -> T { return content } +} + +func pnAppear(perform action: () -> Void) {} + +public func asyncAfter(execute work: () -> Void) {} + +struct ProgressView { + @WiewBuilder var body: Void { + pnAppear(perform: { + #^COMPLETE^#asyncAfter() {} + }) + } +} diff --git a/validation-test/IDE/crashers_2_fixed/0035-closure-solution-application-target-applied-twice-2.swift b/validation-test/IDE/crashers_2_fixed/0035-closure-solution-application-target-applied-twice-2.swift new file mode 100644 index 0000000000000..6645c0a4bd3ed --- /dev/null +++ b/validation-test/IDE/crashers_2_fixed/0035-closure-solution-application-target-applied-twice-2.swift @@ -0,0 +1,20 @@ +// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token COMPLETE + +@resultBuilder public struct WiewBuilder { + static func buildBlock(_ content: T) -> T { return content } +} + +func pnAppear(_ action: () -> Void) {} + +struct BispatchQueue { + static func bsyncAfter(execute work: () -> Void) {} +} + + +public struct ProgressView { + @WiewBuilder var body: Void { + pnAppear({ + BispatchQueue#^COMPLETE^#.bsyncAfter() {} + }) + } +} From 1124248416abe7dd1822e71f24157e3a56d8193a Mon Sep 17 00:00:00 2001 From: Egor Zhdan Date: Mon, 21 Mar 2022 14:47:34 +0000 Subject: [PATCH 232/242] [Docs] Fix dead link to the C++ Interop manifesto --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index a0c91ceb10d61..01d7f6e27e4d7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -249,7 +249,7 @@ documentation, please create a thread on the Swift forums under the Describes the goals and design for Library Evolution. - [BuildManifesto.md](/docs/BuildManifesto.md): Provides an outline for modularizing the build system for the Swift toolchain. -- [CppInteroperabilityManifesto.md](/docs/CppInteroperabilityManifesto.md): +- [CppInteroperabilityManifesto.md](/docs/CppInteroperability/CppInteroperabilityManifesto.md): Describes the motivation and design for first-class Swift-C++ interoperability. - [DifferentiableProgramming.md](/docs/DifferentiableProgramming.md): Outlines a vision and design for first-class differentiable programming in Swift. From 8ee187e0de270841b8d332e8dc706fbd4988bfbe Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 21 Mar 2022 10:20:46 -0400 Subject: [PATCH 233/242] Add regression test for rdar://problem/90506460 --- test/Generics/rdar90506460.swift | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 test/Generics/rdar90506460.swift diff --git a/test/Generics/rdar90506460.swift b/test/Generics/rdar90506460.swift new file mode 100644 index 0000000000000..d42c572b7597b --- /dev/null +++ b/test/Generics/rdar90506460.swift @@ -0,0 +1,27 @@ +// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s + +// CHECK-LABEL: .P1@ +// CHECK-NEXT: Requirement signature: +public protocol P1 { + associatedtype A: P3 where A.C == Self + associatedtype B: P4 where B.C == Self +} + +// CHECK-LABEL: .P2@ +// CHECK-NEXT: Requirement signature: +public protocol P2: Hashable, RawRepresentable, CaseIterable where RawValue == String { + associatedtype C: P1 + associatedtype D: P5 +} + +// CHECK-LABEL: .P3@ +// CHECK-NEXT: Requirement signature: +public protocol P3: P2 {} + +// CHECK-LABEL: .P4@ +// CHECK-NEXT: Requirement signature: +public protocol P4: P2 {} + +// CHECK-LABEL: .P5@ +// CHECK-NEXT: Requirement signature: +public protocol P5: Hashable, RawRepresentable, CaseIterable where RawValue == String {} From f522e566c531706f1e1726ea644c4284c0498e26 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 21 Mar 2022 10:53:28 -0400 Subject: [PATCH 234/242] Add regression test for rdar://problem/90506457 --- test/Generics/rdar90506457.swift | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 test/Generics/rdar90506457.swift diff --git a/test/Generics/rdar90506457.swift b/test/Generics/rdar90506457.swift new file mode 100644 index 0000000000000..bed830d9cb281 --- /dev/null +++ b/test/Generics/rdar90506457.swift @@ -0,0 +1,44 @@ +// RUN: %target-swift-frontend -typecheck %s -debug-generic-signatures -requirement-machine-protocol-signatures=on 2>&1 | %FileCheck %s + +// CHECK-LABEL: .P1@ +// CHECK-NEXT: Requirement signature: > +public protocol P1 { + associatedtype G + associatedtype B: P2 where B.G == G + associatedtype C: P4 where C.G == G + associatedtype D: P5 where D.G == G + associatedtype E: P6 where E.G == G + associatedtype F: P3 where F.Element == SIMD2 +} + +// CHECK-LABEL: .P2@ +// CHECK-NEXT: Requirement signature: +public protocol P2 { + associatedtype G + associatedtype A: P1 where A.G == G + associatedtype C: P4 where C.G == G + associatedtype D: P5 where D.G == G + associatedtype E: P6 where E.G == G +} + +// CHECK-LABEL: .P3@ +// CHECK-NEXT: Requirement signature: +public protocol P3: RandomAccessCollection, MutableCollection where Index == Int {} + +// CHECK-LABEL: .P4@ +// CHECK-NEXT: Requirement signature: +public protocol P4 { + associatedtype G: SIMDScalar +} + +// CHECK-LABEL: .P5@ +// CHECK-NEXT: Requirement signature: +public protocol P5 { + associatedtype G: SIMDScalar +} + +// CHECK-LABEL: .P6@ +// CHECK-NEXT: Requirement signature: +public protocol P6 { + associatedtype G: SIMDScalar +} From 5e36a2a6892e0dd1469899293f6434a84da2b819 Mon Sep 17 00:00:00 2001 From: Andrew Trick Date: Mon, 24 Jan 2022 23:57:13 -0800 Subject: [PATCH 235/242] Fix an AccessedStorage assert for SIL global variables. Allow round-tripping access to global variables. Previously, AccessedStorage asserted that global variables were always associated with a VarDecl. This was to ensure that AccessEnforcmentWMO always recognized the global. Failing to recognize access to a global will cause a miscompile. SILGlobalVariable now has all the information needed by SIL. Particularly, the 'isLet' flag. Simply replace VarDecl with SILGlobalVariable in AccessEnforcmentWMO to eliminate the need for the assert. --- include/swift/SIL/MemAccessUtils.h | 2 +- lib/SIL/Utils/MemAccessUtils.cpp | 13 +--- .../Transforms/AccessEnforcementWMO.cpp | 74 ++++++++++++------- test/SILOptimizer/access_storage_analysis.sil | 22 ++++++ 4 files changed, 73 insertions(+), 38 deletions(-) diff --git a/include/swift/SIL/MemAccessUtils.h b/include/swift/SIL/MemAccessUtils.h index 9d3b90ce11384..0ed0d7f8f93dd 100644 --- a/include/swift/SIL/MemAccessUtils.h +++ b/include/swift/SIL/MemAccessUtils.h @@ -627,7 +627,7 @@ class AccessBase : public AccessRepresentation { return findReferenceRoot(getReference()); } - /// Return the global variable being accessed. + /// Return the global variable being accessed. Always valid. /// /// Precondition: getKind() == Global SILGlobalVariable *getGlobal() const; diff --git a/lib/SIL/Utils/MemAccessUtils.cpp b/lib/SIL/Utils/MemAccessUtils.cpp index 03b908f9c989e..438ed3a56ff5b 100644 --- a/lib/SIL/Utils/MemAccessUtils.cpp +++ b/lib/SIL/Utils/MemAccessUtils.cpp @@ -955,17 +955,10 @@ AccessStorage::AccessStorage(SILValue base, Kind kind) } if (getKind() == AccessBase::Global) { global = getReferencedGlobal(cast(base)); - // Require a decl for all formally accessed globals defined in this - // module. AccessEnforcementWMO requires this. Swift globals defined in - // another module either use an addressor, which has Unidentified - // storage. Imported non-Swift globals are accessed via global_addr but have - // no declaration. - assert(global->getDecl() || isa(base)); - // It's unclear whether a global will ever be missing it's varDecl, but // technically we only preserve it for debug info. So if we don't have a - // decl, check the flag on SILGlobalVariable, which is guaranteed valid, - setLetAccess(getGlobal()->isLet()); + // decl, check the flag on SILGlobalVariable, which is guaranteed valid. + setLetAccess(global->isLet()); return; } value = base; @@ -1001,7 +994,7 @@ const ValueDecl *AccessStorage::getDecl() const { return nullptr; case Class: { // The property index is relative to the VarDecl in ref_element_addr, and - // can only be reliably determined when the base is avaiable. Without the + // can only be reliably determined when the base is available. Without the // base, we can only make a best effort to extract it from the object type, // which might not even be a class in the case of bridge objects. if (ClassDecl *classDecl = diff --git a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp index 128374146bad5..ea9d7846615b4 100644 --- a/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp +++ b/lib/SILOptimizer/Transforms/AccessEnforcementWMO.cpp @@ -66,6 +66,9 @@ using namespace swift; using llvm::DenseMap; using llvm::SmallDenseSet; +using DisjointAccessLocationKey = + llvm::PointerUnion; + // Get the VarDecl that represents the DisjointAccessLocation for the given // storage and access base. Returns nullptr for any storage that can't be // partitioned into a disjoint location. @@ -73,15 +76,18 @@ using llvm::SmallDenseSet; // Global storage is expected to be disjoint because identifyFormalAccess may // only return Unidentified storage for a global variable access if the global // is defined in a different module. -const VarDecl * +static DisjointAccessLocationKey getDisjointAccessLocation(AccessStorageWithBase storageAndBase) { auto storage = storageAndBase.storage; switch (storage.getKind()) { + case AccessStorage::Class: { + auto *varDecl = cast(storageAndBase.getDecl()); + // For class properties, a VarDecl can always be derived from AccessBase. + assert(varDecl && "no VarDecl for class property"); + return varDecl; + } case AccessStorage::Global: - case AccessStorage::Class: - // Class and Globals are always a VarDecl, but the global decl may have a - // null value for global_addr -> phi. - return cast_or_null(storageAndBase.getDecl()); + return storageAndBase.getAccessBase().getGlobal(); case AccessStorage::Box: case AccessStorage::Stack: case AccessStorage::Tail: @@ -95,6 +101,21 @@ getDisjointAccessLocation(AccessStorageWithBase storageAndBase) { llvm_unreachable("unhandled kind"); } +static bool isVisibleExternally(DisjointAccessLocationKey key, SILModule *mod) { + if (auto *decl = key.dyn_cast()) + return mod->isVisibleExternally(decl); + + auto *global = key.get(); + return isPossiblyUsedExternally(global->getLinkage(), mod->isWholeModule()); +} + +static StringRef getName(DisjointAccessLocationKey key) { + if (auto *decl = key.dyn_cast()) + return decl->getNameStr(); + + return key.get()->getName(); +} + namespace { // Implements an optimization to remove access markers on disjoint memory // locations that are never reentrantly accessed. For a given memory location, @@ -134,7 +155,8 @@ class GlobalAccessRemoval { BeginAccessSet beginAccessSet; }; - DenseMap disjointAccessMap; + DenseMap + disjointAccessMap; public: GlobalAccessRemoval(SILModule &module) : module(module) {} @@ -143,9 +165,8 @@ class GlobalAccessRemoval { protected: void visitInstruction(SILInstruction *I); - void recordAccess(SILInstruction *beginAccess, const VarDecl *decl, - AccessStorage::Kind storageKind, - bool hasNoNestedConflict); + void recordAccess(SILInstruction *beginAccess, DisjointAccessLocationKey key, + AccessStorage::Kind storageKind, bool hasNoNestedConflict); void removeNonreentrantAccess(); }; } // namespace @@ -169,15 +190,15 @@ void GlobalAccessRemoval::perform() { void GlobalAccessRemoval::visitInstruction(SILInstruction *I) { if (auto *BAI = dyn_cast(I)) { auto storageAndBase = AccessStorageWithBase::compute(BAI->getSource()); - const VarDecl *decl = getDisjointAccessLocation(storageAndBase); - recordAccess(BAI, decl, storageAndBase.storage.getKind(), + auto key = getDisjointAccessLocation(storageAndBase); + recordAccess(BAI, key, storageAndBase.storage.getKind(), BAI->hasNoNestedConflict()); return; } if (auto *BUAI = dyn_cast(I)) { auto storageAndBase = AccessStorageWithBase::compute(BUAI->getSource()); - const VarDecl *decl = getDisjointAccessLocation(storageAndBase); - recordAccess(BUAI, decl, storageAndBase.storage.getKind(), + auto key = getDisjointAccessLocation(storageAndBase); + recordAccess(BUAI, key, storageAndBase.storage.getKind(), BUAI->hasNoNestedConflict()); return; } @@ -213,21 +234,21 @@ void GlobalAccessRemoval::visitInstruction(SILInstruction *I) { // key_path instruction somewhere else in the same module (or it must be dead // code, or only access public properties). // -// `decl` may be nullptr if the declaration can't be determined from the +// `key` may be nullptr if the variable's identity cannot be determined from the // access. This is only legal when the access is known to be a local access, not // a class property or global. void GlobalAccessRemoval::recordAccess(SILInstruction *beginAccess, - const VarDecl *decl, + DisjointAccessLocationKey key, AccessStorage::Kind storageKind, bool hasNoNestedConflict) { - if (!decl || module.isVisibleExternally(decl)) + if (key.isNull() || isVisibleExternally(key, &module)) return; LLVM_DEBUG(if (!hasNoNestedConflict) llvm::dbgs() - << "Nested conflict on " << decl->getName() << " at" - << *beginAccess << "\n"); + << "Nested conflict on " << getName(key) << " at" << *beginAccess + << "\n"); - auto accessLocIter = disjointAccessMap.find(decl); + auto accessLocIter = disjointAccessMap.find(key); if (accessLocIter != disjointAccessMap.end()) { // Add this begin_access to an existing DisjointAccessLocationInfo. DisjointAccessLocationInfo &info = accessLocIter->second; @@ -245,22 +266,21 @@ void GlobalAccessRemoval::recordAccess(SILInstruction *beginAccess, info.noNestedConflict = hasNoNestedConflict; if (auto *BA = dyn_cast(beginAccess)) info.beginAccessSet.insert(BA); - disjointAccessMap.insert(std::make_pair(decl, info)); + disjointAccessMap.insert(std::make_pair(key, info)); } // For each unique storage within this function that is never reentrantly // accessed, promote all access checks for that storage to static enforcement. void GlobalAccessRemoval::removeNonreentrantAccess() { - for (auto &declAndInfo : disjointAccessMap) { - const DisjointAccessLocationInfo &info = declAndInfo.second; + for (auto &keyAndInfo : disjointAccessMap) { + const DisjointAccessLocationInfo &info = keyAndInfo.second; if (!info.noNestedConflict) continue; - const VarDecl *decl = declAndInfo.first; - LLVM_DEBUG(llvm::dbgs() << "Eliminating all formal access on " - << decl->getName() << "\n"); - assert(!module.isVisibleExternally(decl)); - (void)decl; + auto key = keyAndInfo.first; + LLVM_DEBUG(llvm::dbgs() + << "Eliminating all formal access on " << getName(key) << "\n"); + assert(!isVisibleExternally(key, &module)); // Non-deterministic iteration, only used to set a flag. for (BeginAccessInst *beginAccess : info.beginAccessSet) { diff --git a/test/SILOptimizer/access_storage_analysis.sil b/test/SILOptimizer/access_storage_analysis.sil index 727217a5ce2e0..a85dd442dc7ff 100644 --- a/test/SILOptimizer/access_storage_analysis.sil +++ b/test/SILOptimizer/access_storage_analysis.sil @@ -723,3 +723,25 @@ bb3(%7 : $Builtin.RawPointer): end_access %9 : $*Int64 return %10 : $Int64 } + +// Test storage for SIL global variable declations. + +sil_global hidden @testGlobal : $Builtin.Int64 + +sil hidden [global_init] @testAddressor : $@convention(thin) () -> Builtin.RawPointer { +bb0: + %4 = global_addr @testGlobal : $*Builtin.Int64 + %5 = address_to_pointer %4 : $*Builtin.Int64 to $Builtin.RawPointer + return %5 : $Builtin.RawPointer +} + +sil hidden [transparent] @testGetter : $@convention(thin) () -> Builtin.Int64 { +bb0: + %2 = function_ref @testAddressor : $@convention(thin) () -> Builtin.RawPointer + %3 = apply %2() : $@convention(thin) () -> Builtin.RawPointer + %4 = pointer_to_address %3 : $Builtin.RawPointer to [strict] $*Builtin.Int64 + %5 = begin_access [read] [dynamic] %4 : $*Builtin.Int64 + %6 = load %5 : $*Builtin.Int64 + end_access %5 : $*Builtin.Int64 + return %6 : $Builtin.Int64 +} From cca936e790706475c6182467e6840912f5bb8cac Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 18 Mar 2022 23:06:39 -0400 Subject: [PATCH 236/242] RequirementMachine: Don't consider protocol typealiases with UnboundGenericType These are stealth generic typealiases and should not participate in the rewrite system. --- .../RequirementLowering.cpp | 38 +++++++++++-------- lib/AST/RequirementMachine/Symbol.cpp | 1 + .../protocol_typealias_unbound_generic.swift | 7 ++++ 3 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 test/Generics/protocol_typealias_unbound_generic.swift diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 297a7af55caf8..108c9fb0824ca 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -705,7 +705,7 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator, // underlying type of the typealias. if (auto *typeAliasDecl = dyn_cast(decl)) { if (!typeAliasDecl->isGeneric()) { - // Ignore the typealias if we have an associated type with the same anme + // Ignore the typealias if we have an associated type with the same name // in the same protocol. This is invalid anyway, but it's just here to // ensure that we produce the same requirement signature on some tests // with -requirement-machine-protocol-signatures=verify. @@ -713,6 +713,8 @@ StructuralRequirementsRequest::evaluate(Evaluator &evaluator, continue; auto underlyingType = typeAliasDecl->getStructuralType(); + if (underlyingType->is()) + continue; auto subjectType = DependentMemberType::get( selfTy, typeAliasDecl->getName()); @@ -752,6 +754,20 @@ TypeAliasRequirementsRequest::evaluate(Evaluator &evaluator, (ctx.LangOpts.RequirementMachineProtocolSignatures == RequirementMachineMode::Enabled); + auto getStructuralType = [](TypeDecl *typeDecl) -> Type { + if (auto typealias = dyn_cast(typeDecl)) { + if (typealias->getUnderlyingTypeRepr() != nullptr) { + auto type = typealias->getStructuralType(); + if (auto *aliasTy = cast(type.getPointer())) + return aliasTy->getSinglyDesugaredType(); + return type; + } + return typealias->getUnderlyingType(); + } + + return typeDecl->getDeclaredInterfaceType(); + }; + // Collect all typealiases from inherited protocols recursively. llvm::MapVector> inheritedTypeDecls; for (auto *inheritedProto : ctx.getRewriteContext().getInheritedProtocols(proto)) { @@ -762,25 +778,17 @@ TypeAliasRequirementsRequest::evaluate(Evaluator &evaluator, if (genReq->getGenericParams()) continue; + // Ignore typealiases with UnboundGenericType, since they + // are like generic typealiases. + if (auto *typeAlias = dyn_cast(req)) + if (getStructuralType(typeAlias)->is()) + continue; + inheritedTypeDecls[typeReq->getName()].push_back(typeReq); } } } - auto getStructuralType = [](TypeDecl *typeDecl) -> Type { - if (auto typealias = dyn_cast(typeDecl)) { - if (typealias->getUnderlyingTypeRepr() != nullptr) { - auto type = typealias->getStructuralType(); - if (auto *aliasTy = cast(type.getPointer())) - return aliasTy->getSinglyDesugaredType(); - return type; - } - return typealias->getUnderlyingType(); - } - - return typeDecl->getDeclaredInterfaceType(); - }; - // An inferred same-type requirement between the two type declarations // within this protocol or a protocol it inherits. auto recordInheritedTypeRequirement = [&](TypeDecl *first, TypeDecl *second) { diff --git a/lib/AST/RequirementMachine/Symbol.cpp b/lib/AST/RequirementMachine/Symbol.cpp index aaed85436786a..448fec061a793 100644 --- a/lib/AST/RequirementMachine/Symbol.cpp +++ b/lib/AST/RequirementMachine/Symbol.cpp @@ -78,6 +78,7 @@ struct Symbol::Storage final Storage(Symbol::Kind kind, CanType type, ArrayRef substitutions) { assert(kind == Symbol::Kind::Superclass || kind == Symbol::Kind::ConcreteType); + assert(!type->hasUnboundGenericType()); assert(!type->hasTypeVariable()); assert(type->hasTypeParameter() != substitutions.empty()); diff --git a/test/Generics/protocol_typealias_unbound_generic.swift b/test/Generics/protocol_typealias_unbound_generic.swift new file mode 100644 index 0000000000000..4848cb930f0fe --- /dev/null +++ b/test/Generics/protocol_typealias_unbound_generic.swift @@ -0,0 +1,7 @@ +// RUN: %target-typecheck-verify-swift + +protocol P { + typealias A = Array + + associatedtype X where X == A // expected-error {{reference to generic type 'Self.A' (aka 'Array') requires arguments in <...>}} +} From 404eb2cd2cf284f7a9b188345d5e5c0b01b8c34e Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 21 Mar 2022 13:35:33 -0400 Subject: [PATCH 237/242] GSB: Relax verify check some more in RequirementSignatureRequest The GSB sometimes emits redundant same-type requirements here. Only checking for a prefix would still trigger false positives, so relax it all the way to only consider conformance requirements, which are the important ones from an ABI standpoint. --- lib/AST/GenericSignatureBuilder.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/AST/GenericSignatureBuilder.cpp b/lib/AST/GenericSignatureBuilder.cpp index 1a8274e7354a1..ab3f29c8621c5 100644 --- a/lib/AST/GenericSignatureBuilder.cpp +++ b/lib/AST/GenericSignatureBuilder.cpp @@ -8664,21 +8664,32 @@ RequirementSignatureRequest::evaluate(Evaluator &evaluator, ArrayRef gsbResult) { if (rqmResult.size() > gsbResult.size()) return false; + + SmallVector rqmNonSameType; + SmallVector gsbNonSameType; + + for (auto req : rqmResult) { + if (req.getKind() != RequirementKind::SameType) + rqmNonSameType.push_back(req); + } + + for (auto req : gsbResult) { + if (req.getKind() != RequirementKind::SameType) + gsbNonSameType.push_back(req); + } + + if (rqmNonSameType.size() != gsbNonSameType.size()) + return false; - if (!std::equal(rqmResult.begin(), - rqmResult.end(), - gsbResult.begin(), + if (!std::equal(rqmNonSameType.begin(), + rqmNonSameType.end(), + gsbNonSameType.begin(), [](const Requirement &lhs, const Requirement &rhs) { return lhs.getCanonical() == rhs.getCanonical(); })) return false; - for (auto req : gsbResult.slice(rqmResult.size())) { - if (req.getKind() != RequirementKind::SameType) - return false; - } - return true; }; From 6438c7e931e06f23503732a279d67feb4f7edb6a Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 21 Mar 2022 10:53:59 -0700 Subject: [PATCH 238/242] Conservative Dynamic Casts to/from Parameterized Protocols Update cast feasibility analysis to take parameterized protocols into account. The cast classifier was used to being able to essentially just run `conformsToProtocol` on the existential type, but for parameterized protocol types this will ignore the extra requirements imposed by the argument clause. For now, route everything through the runtime. --- lib/SIL/Utils/DynamicCasts.cpp | 17 ++++ .../cast_folding_parameterized_protocol.swift | 81 +++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100644 test/SILOptimizer/cast_folding_parameterized_protocol.swift diff --git a/lib/SIL/Utils/DynamicCasts.cpp b/lib/SIL/Utils/DynamicCasts.cpp index ae167f9c9ec57..72232a123c26b 100644 --- a/lib/SIL/Utils/DynamicCasts.cpp +++ b/lib/SIL/Utils/DynamicCasts.cpp @@ -78,6 +78,15 @@ static bool canClassOrSuperclassesHaveUnknownSubclasses(ClassDecl *CD, return false; } +static CanType unwrapExistential(CanType e) { + assert(e.isExistentialType()); + + if (auto et = dyn_cast(e)) + return et.getConstraintType(); + + return e; +} + /// Try to classify a conversion from non-existential type /// into an existential type by performing a static check /// of protocol conformances if it is possible. @@ -94,6 +103,14 @@ classifyDynamicCastToProtocol(ModuleDecl *M, CanType source, CanType target, if (!TargetProtocol) return DynamicCastFeasibility::MaySucceed; + // If the target is a parameterized protocol type, conformsToProtocol + // is insufficient to prove the feasibility of the cast as it does not + // check the additional requirements. + // FIXME: This is a weak predicate that doesn't take into account + // class compositions - since any C & P doesn't work yet anyways. + if (isa(unwrapExistential(target))) + return DynamicCastFeasibility::MaySucceed; + // If conformsToProtocol returns a valid conformance, then all requirements // were proven by the type checker. if (M->conformsToProtocol(source, TargetProtocol)) diff --git a/test/SILOptimizer/cast_folding_parameterized_protocol.swift b/test/SILOptimizer/cast_folding_parameterized_protocol.swift new file mode 100644 index 0000000000000..ae46bd58b8c6e --- /dev/null +++ b/test/SILOptimizer/cast_folding_parameterized_protocol.swift @@ -0,0 +1,81 @@ +// RUN: %target-swift-frontend %s -emit-sil -enable-parameterized-protocol-types -O -o - | %FileCheck %s + +public protocol P {} +public protocol Q: P where T == Int {} +public struct X: Q { + public typealias T = Int +} +public struct Y: P {} +extension Y: Q where T == Int {} + +@_optimize(none) +func use(_ t: T) {} + +// CHECK-LABEL: sil @$s35cast_folding_parameterized_protocol23concrete_to_existentialyyAA1XV_AA1YVyxGAFySiGtlF : $@convention(thin) (X, Y, Y) -> () +public func concrete_to_existential(_ x: X, _ yt: Y, _ yi: Y) { + // CHECK:{{%.*}} = init_existential_addr %6 : $*P, $X + use(x as! any P) + // CHECK: unconditional_checked_cast_addr X in {{%.*}} : $*X to P in {{%.*}} : $*P + use(x as! any P) + // CHECK: unconditional_checked_cast_addr X in {{%.*}} : $*X to P in {{%.*}} : $*P + use(x as! any P) + // CHECK: unconditional_checked_cast_addr X in {{%.*}} : $*X to P in {{%.*}} : $*P + use(x as! any P) + // CHECK: {{%.*}} = init_existential_addr {{%.*}} : $*Q, $X + use(x as! any Q) + + // CHECK: {{%.*}} = init_existential_addr {{%.*}} : $*P, $Y + use(yt as! any P) + // CHECK: unconditional_checked_cast_addr Y in {{%.*}} : $*Y to P in {{%.*}} : $*P + use(yt as! any P) + // CHECK: unconditional_checked_cast_addr Y in {{%.*}} : $*Y to P in {{%.*}} : $*P + use(yt as! any P) + // CHECK: unconditional_checked_cast_addr Y in {{%.*}} : $*Y to P in {{%.*}} : $*P + use(yt as! any P) + // CHECK: unconditional_checked_cast_addr Y in {{%.*}} : $*Y to Q in {{%.*}} : $*Q + use(yt as! any Q) + + // CHECK: {{%.*}} = init_existential_addr {{%.*}} : $*P, $Y + use(yi as! any P) + // CHECK: unconditional_checked_cast_addr Y in {{%.*}} : $*Y to P in {{%.*}} : $*P + use(yi as! any P) + // CHECK: unconditional_checked_cast_addr Y in {{%.*}} : $*Y to P in {{%.*}} : $*P + use(yi as! any P) + // CHECK: unconditional_checked_cast_addr Y in {{%.*}} : $*Y to P in {{%.*}} : $*P + use(yi as! any P) + // CHECK: {{%.*}} = init_existential_addr {{%.*}} : $*Q, $Y + use(yi as! any Q) +} + +// CHECK-LABEL: sil @$s35cast_folding_parameterized_protocol23existential_to_concreteyyxm_AA1P_pyxXPtlF : $@convention(thin) (@thick T.Type, @in_guaranteed P) -> () +public func existential_to_concrete(_: T.Type, _ p: any P) { + // CHECK: unconditional_checked_cast_addr P in {{%.*}} : $*P to X in {{%.*}} : $*X + _ = p as! X + // CHECK: unconditional_checked_cast_addr P in {{%.*}} : $*P to Y in {{%.*}} : $*Y + _ = p as! Y + // CHECK: unconditional_checked_cast_addr P in {{%.*}} : $*P to Y in {{%.*}} : $*Y + _ = p as! Y + // CHECK: unconditional_checked_cast_addr P in {{%.*}} : $*P to Y in {{%.*}} : $*Y + _ = p as! Y +} + +// CHECK-LABEL: sil @$s35cast_folding_parameterized_protocol015existential_to_E0yyAA1P_pyxXP_AA1Q_ptlF : $@convention(thin) (@in_guaranteed P, @in_guaranteed Q) -> () +public func existential_to_existential(_ p: any P, _ q: any Q) { + // CHECK: unconditional_checked_cast_addr P in {{%.*}} : $*P to Q in {{%.*}} : $*Q + _ = p as! any Q + // CHECK: unconditional_checked_cast_addr P in {{%.*}} : $*P to P in {{%.*}} : $*P + _ = p as! any P + // CHECK: unconditional_checked_cast_addr P in {{%.*}} : $*P to P in {{%.*}} : $*P + _ = p as! any P + // CHECK: unconditional_checked_cast_addr P in {{%.*}} : $*P to P in {{%.*}} : $*P + _ = p as! any P + + // CHECK: unconditional_checked_cast_addr Q in {{%.*}} : $*Q to P in {{%.*}} : $*P + _ = q as! any P + // CHECK: unconditional_checked_cast_addr Q in {{%.*}} : $*Q to P in {{%.*}} : $*P + _ = q as! any P + // CHECK: unconditional_checked_cast_addr Q in {{%.*}} : $*Q to P in {{%.*}} : $*P + _ = q as! any P + // CHECK: unconditional_checked_cast_addr Q in {{%.*}} : $*Q to P in {{%.*}} : $*P + _ = q as! any P +} From 86f9162606f57f3f4e4a006cd0cf0d62e2b0b62a Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Fri, 18 Mar 2022 14:38:46 -0700 Subject: [PATCH 239/242] [PrintAsClang] NFC, refactor primitive type mapping logic out from DeclAndTypePrinter This will allow us to use type mappings in the C ABI function printer. --- lib/PrintAsClang/CMakeLists.txt | 1 + lib/PrintAsClang/DeclAndTypePrinter.cpp | 102 +------------------- lib/PrintAsClang/DeclAndTypePrinter.h | 22 ++--- lib/PrintAsClang/ModuleContentsWriter.cpp | 5 +- lib/PrintAsClang/PrimitiveTypeMapping.cpp | 112 ++++++++++++++++++++++ lib/PrintAsClang/PrimitiveTypeMapping.h | 63 ++++++++++++ 6 files changed, 191 insertions(+), 114 deletions(-) create mode 100644 lib/PrintAsClang/PrimitiveTypeMapping.cpp create mode 100644 lib/PrintAsClang/PrimitiveTypeMapping.h diff --git a/lib/PrintAsClang/CMakeLists.txt b/lib/PrintAsClang/CMakeLists.txt index 72b0f5533e248..155c232b4161e 100644 --- a/lib/PrintAsClang/CMakeLists.txt +++ b/lib/PrintAsClang/CMakeLists.txt @@ -3,6 +3,7 @@ add_swift_host_library(swiftPrintAsClang STATIC CxxSynthesis.cpp DeclAndTypePrinter.cpp ModuleContentsWriter.cpp + PrimitiveTypeMapping.cpp PrintAsClang.cpp) target_link_libraries(swiftPrintAsClang PRIVATE swiftAST diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index 5c8c82ae0b0bf..82030dcc926a1 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -12,6 +12,7 @@ #include "DeclAndTypePrinter.h" #include "CxxSynthesis.h" +#include "PrimitiveTypeMapping.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTMangler.h" @@ -1442,7 +1443,7 @@ class DeclAndTypePrinter::Implementation /// Otherwise returns null. const ClassDecl *getObjCBridgedClass(const NominalTypeDecl *nominal) { // Print known types as their unbridged type. - if (getKnownTypeInfo(nominal)) + if (owningPrinter.typeMapping.getKnownObjCTypeInfo(nominal)) return nullptr; // Print imported bridgeable decls as their unbridged type. @@ -1546,102 +1547,6 @@ class DeclAndTypePrinter::Implementation return false; } - /// If \p typeDecl is one of the standard library types used to map in Clang - /// primitives and basic types, return the info in \c specialNames containing - /// the Clang name and whether it can be nullable in C. - Optional getKnownTypeInfo(const TypeDecl *typeDecl) { - auto &specialNames = owningPrinter.specialNames; - if (specialNames.empty()) { - ASTContext &ctx = getASTContext(); -#define MAP(SWIFT_NAME, CLANG_REPR, NEEDS_NULLABILITY) \ - specialNames[{ctx.StdlibModuleName, ctx.getIdentifier(#SWIFT_NAME)}] = \ - { CLANG_REPR, NEEDS_NULLABILITY } - - MAP(CBool, "bool", false); - - MAP(CChar, "char", false); - MAP(CWideChar, "wchar_t", false); - MAP(CChar16, "char16_t", false); - MAP(CChar32, "char32_t", false); - - MAP(CSignedChar, "signed char", false); - MAP(CShort, "short", false); - MAP(CInt, "int", false); - MAP(CLong, "long", false); - MAP(CLongLong, "long long", false); - - MAP(CUnsignedChar, "unsigned char", false); - MAP(CUnsignedShort, "unsigned short", false); - MAP(CUnsignedInt, "unsigned int", false); - MAP(CUnsignedLong, "unsigned long", false); - MAP(CUnsignedLongLong, "unsigned long long", false); - - MAP(CFloat, "float", false); - MAP(CDouble, "double", false); - - MAP(Int8, "int8_t", false); - MAP(Int16, "int16_t", false); - MAP(Int32, "int32_t", false); - MAP(Int64, "int64_t", false); - MAP(UInt8, "uint8_t", false); - MAP(UInt16, "uint16_t", false); - MAP(UInt32, "uint32_t", false); - MAP(UInt64, "uint64_t", false); - - MAP(Float, "float", false); - MAP(Double, "double", false); - MAP(Float32, "float", false); - MAP(Float64, "double", false); - - MAP(Int, "NSInteger", false); - MAP(UInt, "NSUInteger", false); - MAP(Bool, "BOOL", false); - - MAP(OpaquePointer, "void *", true); - MAP(UnsafeRawPointer, "void const *", true); - MAP(UnsafeMutableRawPointer, "void *", true); - - Identifier ID_ObjectiveC = ctx.Id_ObjectiveC; - specialNames[{ID_ObjectiveC, ctx.getIdentifier("ObjCBool")}] - = { "BOOL", false}; - specialNames[{ID_ObjectiveC, ctx.getIdentifier("Selector")}] - = { "SEL", true }; - specialNames[{ID_ObjectiveC, - ctx.getIdentifier( - ctx.getSwiftName(KnownFoundationEntity::NSZone))}] - = { "struct _NSZone *", true }; - - specialNames[{ctx.Id_Darwin, ctx.getIdentifier("DarwinBoolean")}] - = { "Boolean", false}; - - specialNames[{ctx.Id_CoreGraphics, ctx.Id_CGFloat}] - = { "CGFloat", false }; - - specialNames[{ctx.Id_CoreFoundation, ctx.Id_CGFloat}] - = { "CGFloat", false }; - - // Use typedefs we set up for SIMD vector types. -#define MAP_SIMD_TYPE(BASENAME, _, __) \ - specialNames[{ctx.Id_simd, ctx.getIdentifier(#BASENAME "2")}] \ - = { "swift_" #BASENAME "2", false }; \ - specialNames[{ctx.Id_simd, ctx.getIdentifier(#BASENAME "3")}] \ - = { "swift_" #BASENAME "3", false }; \ - specialNames[{ctx.Id_simd, ctx.getIdentifier(#BASENAME "4")}] \ - = { "swift_" #BASENAME "4", false }; -#include "swift/ClangImporter/SIMDMappedTypes.def" - static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, - "must add or remove special name mappings if max number of " - "SIMD elements is changed"); - } - - Identifier moduleName = typeDecl->getModuleContext()->getName(); - Identifier name = typeDecl->getName(); - auto iter = specialNames.find({moduleName, name}); - if (iter == specialNames.end()) - return None; - return iter->second; - } - /// If \p typeDecl is one of the standard library types used to map in Clang /// primitives and basic types, print out the appropriate spelling and /// return true. @@ -1650,7 +1555,8 @@ class DeclAndTypePrinter::Implementation /// for interfacing with C and Objective-C. bool printIfKnownSimpleType(const TypeDecl *typeDecl, Optional optionalKind) { - Optional knownTypeInfo = getKnownTypeInfo(typeDecl); + auto knownTypeInfo = + owningPrinter.typeMapping.getKnownObjCTypeInfo(typeDecl); if (!knownTypeInfo) return false; os << knownTypeInfo->name; diff --git a/lib/PrintAsClang/DeclAndTypePrinter.h b/lib/PrintAsClang/DeclAndTypePrinter.h index 5a79d83871d1c..72eff35d19c7a 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.h +++ b/lib/PrintAsClang/DeclAndTypePrinter.h @@ -25,6 +25,8 @@ namespace clang { namespace swift { +class PrimitiveTypeMapping; + /// Responsible for printing a Swift Decl or Type in Objective-C, to be /// included in a Swift module's ObjC compatibility header. class DeclAndTypePrinter { @@ -39,22 +41,10 @@ class DeclAndTypePrinter { raw_ostream &os; raw_ostream &prologueOS; const DelayedMemberSet &delayedMembers; + PrimitiveTypeMapping &typeMapping; AccessLevel minRequiredAccess; OutputLanguageMode outputLang; - struct CTypeInfo { - StringRef name; - bool canBeNullable; - }; - - /// A map from {Module, TypeName} pairs to {C name, C nullability} pairs. - /// - /// This is populated on first use with a list of known Swift types that are - /// translated directly by the ObjC printer instead of structurally, allowing - /// it to do things like map 'Int' to 'NSInteger' and 'Float' to 'float'. - /// In some sense it's the reverse of the ClangImporter's MappedTypes.def. - llvm::DenseMap, CTypeInfo> specialNames; - /// The name 'CFTypeRef'. /// /// Cached for convenience. @@ -64,10 +54,12 @@ class DeclAndTypePrinter { public: DeclAndTypePrinter(ModuleDecl &mod, raw_ostream &out, raw_ostream &prologueOS, - DelayedMemberSet &delayed, AccessLevel access, + DelayedMemberSet &delayed, + PrimitiveTypeMapping &typeMapping, AccessLevel access, OutputLanguageMode outputLang) : M(mod), os(out), prologueOS(prologueOS), delayedMembers(delayed), - minRequiredAccess(access), outputLang(outputLang) {} + typeMapping(typeMapping), minRequiredAccess(access), + outputLang(outputLang) {} /// Returns true if \p VD should be included in a compatibility header for /// the options the printer was constructed with. diff --git a/lib/PrintAsClang/ModuleContentsWriter.cpp b/lib/PrintAsClang/ModuleContentsWriter.cpp index 682a712cbcb1f..7247641f5b40d 100644 --- a/lib/PrintAsClang/ModuleContentsWriter.cpp +++ b/lib/PrintAsClang/ModuleContentsWriter.cpp @@ -15,6 +15,7 @@ #include "CxxSynthesis.h" #include "DeclAndTypePrinter.h" #include "OutputLanguageMode.h" +#include "PrimitiveTypeMapping.h" #include "swift/AST/ExistentialLayout.h" #include "swift/AST/Module.h" @@ -123,6 +124,7 @@ class ModuleWriter { llvm::DenseMap> seenTypes; std::vector declsToWrite; DelayedMemberSet delayedMembers; + PrimitiveTypeMapping typeMapping; DeclAndTypePrinter printer; OutputLanguageMode outputLangMode; @@ -131,7 +133,8 @@ class ModuleWriter { llvm::SmallPtrSetImpl &imports, ModuleDecl &mod, AccessLevel access, OutputLanguageMode outputLang) : os(os), imports(imports), M(mod), - printer(M, os, prologueOS, delayedMembers, access, outputLang), + printer(M, os, prologueOS, delayedMembers, typeMapping, access, + outputLang), outputLangMode(outputLang) {} /// Returns true if we added the decl's module to the import set, false if diff --git a/lib/PrintAsClang/PrimitiveTypeMapping.cpp b/lib/PrintAsClang/PrimitiveTypeMapping.cpp new file mode 100644 index 0000000000000..221914151faf4 --- /dev/null +++ b/lib/PrintAsClang/PrimitiveTypeMapping.cpp @@ -0,0 +1,112 @@ +//===--- PrimitiveTypeMapping.cpp - Mapping primitive types -----*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#include "PrimitiveTypeMapping.h" +#include "swift/AST/ASTContext.h" +#include "swift/AST/Decl.h" +#include "swift/AST/Module.h" +#include "swift/ClangImporter/ClangImporter.h" + +using namespace swift; + +void PrimitiveTypeMapping::initialize(ASTContext &ctx) { + assert(mappedTypeNames.empty() && "expected empty type map"); +#define MAP(SWIFT_NAME, CLANG_REPR, NEEDS_NULLABILITY) \ + mappedTypeNames[{ctx.StdlibModuleName, ctx.getIdentifier(#SWIFT_NAME)}] = { \ + CLANG_REPR, NEEDS_NULLABILITY} + + MAP(CBool, "bool", false); + + MAP(CChar, "char", false); + MAP(CWideChar, "wchar_t", false); + MAP(CChar16, "char16_t", false); + MAP(CChar32, "char32_t", false); + + MAP(CSignedChar, "signed char", false); + MAP(CShort, "short", false); + MAP(CInt, "int", false); + MAP(CLong, "long", false); + MAP(CLongLong, "long long", false); + + MAP(CUnsignedChar, "unsigned char", false); + MAP(CUnsignedShort, "unsigned short", false); + MAP(CUnsignedInt, "unsigned int", false); + MAP(CUnsignedLong, "unsigned long", false); + MAP(CUnsignedLongLong, "unsigned long long", false); + + MAP(CFloat, "float", false); + MAP(CDouble, "double", false); + + MAP(Int8, "int8_t", false); + MAP(Int16, "int16_t", false); + MAP(Int32, "int32_t", false); + MAP(Int64, "int64_t", false); + MAP(UInt8, "uint8_t", false); + MAP(UInt16, "uint16_t", false); + MAP(UInt32, "uint32_t", false); + MAP(UInt64, "uint64_t", false); + + MAP(Float, "float", false); + MAP(Double, "double", false); + MAP(Float32, "float", false); + MAP(Float64, "double", false); + + MAP(Int, "NSInteger", false); + MAP(UInt, "NSUInteger", false); + MAP(Bool, "BOOL", false); + + MAP(OpaquePointer, "void *", true); + MAP(UnsafeRawPointer, "void const *", true); + MAP(UnsafeMutableRawPointer, "void *", true); + + Identifier ID_ObjectiveC = ctx.Id_ObjectiveC; + mappedTypeNames[{ID_ObjectiveC, ctx.getIdentifier("ObjCBool")}] = {"BOOL", + false}; + mappedTypeNames[{ID_ObjectiveC, ctx.getIdentifier("Selector")}] = {"SEL", + true}; + mappedTypeNames[{ID_ObjectiveC, ctx.getIdentifier(ctx.getSwiftName( + KnownFoundationEntity::NSZone))}] = { + "struct _NSZone *", true}; + + mappedTypeNames[{ctx.Id_Darwin, ctx.getIdentifier("DarwinBoolean")}] = { + "Boolean", false}; + + mappedTypeNames[{ctx.Id_CoreGraphics, ctx.Id_CGFloat}] = {"CGFloat", false}; + + mappedTypeNames[{ctx.Id_CoreFoundation, ctx.Id_CGFloat}] = {"CGFloat", false}; + + // Use typedefs we set up for SIMD vector types. +#define MAP_SIMD_TYPE(BASENAME, _, __) \ + mappedTypeNames[{ctx.Id_simd, ctx.getIdentifier(#BASENAME "2")}] = { \ + "swift_" #BASENAME "2", false}; \ + mappedTypeNames[{ctx.Id_simd, ctx.getIdentifier(#BASENAME "3")}] = { \ + "swift_" #BASENAME "3", false}; \ + mappedTypeNames[{ctx.Id_simd, ctx.getIdentifier(#BASENAME "4")}] = { \ + "swift_" #BASENAME "4", false}; +#include "swift/ClangImporter/SIMDMappedTypes.def" + static_assert(SWIFT_MAX_IMPORTED_SIMD_ELEMENTS == 4, + "must add or remove special name mappings if max number of " + "SIMD elements is changed"); +} + +Optional +PrimitiveTypeMapping::getKnownObjCTypeInfo(const TypeDecl *typeDecl) { + if (mappedTypeNames.empty()) + initialize(typeDecl->getASTContext()); + + Identifier moduleName = typeDecl->getModuleContext()->getName(); + Identifier name = typeDecl->getName(); + auto iter = mappedTypeNames.find({moduleName, name}); + if (iter == mappedTypeNames.end()) + return None; + return ObjCClangTypeInfo{iter->second.objcName, iter->second.canBeNullable}; +} diff --git a/lib/PrintAsClang/PrimitiveTypeMapping.h b/lib/PrintAsClang/PrimitiveTypeMapping.h new file mode 100644 index 0000000000000..85ceca10ff68f --- /dev/null +++ b/lib/PrintAsClang/PrimitiveTypeMapping.h @@ -0,0 +1,63 @@ +//===--- PrimitiveTypeMapping.h - Mapping primitive types -------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_PRINTASCLANG_PRIMITIVETYPEMAPPING_H +#define SWIFT_PRINTASCLANG_PRIMITIVETYPEMAPPING_H + +#include "swift/AST/Identifier.h" +#include "swift/Basic/LLVM.h" +#include "llvm/ADT/DenseMap.h" + +namespace swift { + +class ASTContext; +class TypeDecl; + +/// Provides a mapping from Swift's primitive types to C / Objective-C / C++ +/// primitive types. +/// +/// Certain types have mappings that differ in different language modes. +/// For example, Swift's `Int` maps to `NSInteger` for Objective-C declarations, +/// but to something like `intptr_t` or `swift::Int` for C and C++ declarations. +class PrimitiveTypeMapping { +public: + struct ObjCClangTypeInfo { + StringRef name; + bool canBeNullable; + }; + + /// Returns the Objective-C type name and nullability for the given Swift + /// primitive type declaration, or \c None if no such type name exists. + Optional getKnownObjCTypeInfo(const TypeDecl *typeDecl); + +private: + void initialize(ASTContext &ctx); + + struct ClangTypeInfo { + // The Objective-C name of the Swift type. + StringRef objcName; + bool canBeNullable; + }; + + /// A map from {Module, TypeName} pairs to {C name, C nullability} pairs. + /// + /// This is populated on first use with a list of known Swift types that are + /// translated directly by the ObjC printer instead of structurally, allowing + /// it to do things like map 'Int' to 'NSInteger' and 'Float' to 'float'. + /// In some sense it's the reverse of the ClangImporter's MappedTypes.def. + llvm::DenseMap, ClangTypeInfo> + mappedTypeNames; +}; + +} // end namespace swift + +#endif From 298a84a20225eeb5997e8ef1519e2471a98d87bb Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 21 Mar 2022 16:16:57 -0400 Subject: [PATCH 240/242] RequirementMachine: Preserve sugar in desugarSameTypeRequirement() --- lib/AST/RequirementMachine/RequirementLowering.cpp | 4 ++-- test/Generics/requirement_machine_diagnostics.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/AST/RequirementMachine/RequirementLowering.cpp b/lib/AST/RequirementMachine/RequirementLowering.cpp index 108c9fb0824ca..74a81aecd35a2 100644 --- a/lib/AST/RequirementMachine/RequirementLowering.cpp +++ b/lib/AST/RequirementMachine/RequirementLowering.cpp @@ -73,13 +73,13 @@ static void desugarSameTypeRequirement(Type lhs, Type rhs, SourceLoc loc, if (secondType->isTypeParameter()) { result.emplace_back(RequirementKind::SameType, - secondType, firstType); + secondType, sugaredFirstType); recordedRequirements = true; return true; } errors.push_back( - RequirementError::forConcreteTypeMismatch(firstType, + RequirementError::forConcreteTypeMismatch(sugaredFirstType, secondType, loc)); recordedErrors = true; diff --git a/test/Generics/requirement_machine_diagnostics.swift b/test/Generics/requirement_machine_diagnostics.swift index 9b6b1ea7935c9..e6aa1fa2b9721 100644 --- a/test/Generics/requirement_machine_diagnostics.swift +++ b/test/Generics/requirement_machine_diagnostics.swift @@ -19,7 +19,7 @@ typealias NotAnInt = Double protocol X {} -// expected-error@+1{{generic signature requires types 'Double' and 'Int' to be the same}} +// expected-error@+1{{generic signature requires types 'NotAnInt' (aka 'Double') and 'Int' to be the same}} extension X where NotAnInt == Int {} protocol EqualComparable { From 3571fa8860c60338b1248b83efa703e23c21841a Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 21 Mar 2022 16:17:13 -0400 Subject: [PATCH 241/242] AST: Preserve sugar in TypeMatcher::visitBoundGenericType() --- include/swift/AST/TypeMatcher.h | 5 ++--- test/Generics/requirement_machine_diagnostics.swift | 4 ++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/swift/AST/TypeMatcher.h b/include/swift/AST/TypeMatcher.h index 5b02382a1a91b..24c51e1f3f244 100644 --- a/include/swift/AST/TypeMatcher.h +++ b/include/swift/AST/TypeMatcher.h @@ -419,9 +419,8 @@ class TypeMatcher { bool visitBoundGenericType(CanBoundGenericType firstBGT, Type secondType, Type sugaredFirstType) { - auto _secondBGT = secondType->getCanonicalType(); - if (firstBGT->getKind() == _secondBGT->getKind()) { - auto secondBGT = cast(_secondBGT); + if (firstBGT->getKind() == secondType->getDesugaredType()->getKind()) { + auto secondBGT = secondType->castTo(); if (firstBGT->getDecl() != secondBGT->getDecl()) return mismatch(firstBGT.getPointer(), secondBGT, sugaredFirstType); diff --git a/test/Generics/requirement_machine_diagnostics.swift b/test/Generics/requirement_machine_diagnostics.swift index e6aa1fa2b9721..622f64264fe6a 100644 --- a/test/Generics/requirement_machine_diagnostics.swift +++ b/test/Generics/requirement_machine_diagnostics.swift @@ -194,3 +194,7 @@ func inferred3(_: T) where T.X : Hashable, T.Z == Set, T.X == T.Y func inferred4(_: T) where T.Z == Set, T.X : Hashable, T.X == T.Y {} func inferred5(_: T) where T.Z == Set, T.Y : Hashable, T.X == T.Y {} func inferred6(_: T) where T.Y : Hashable, T.Z == Set, T.X == T.Y {} + +func typeMatcherSugar(_: T) where Array == Array, Array == Array {} +// expected-warning@-1 2{{redundant same-type constraint 'Array' == 'Array'}} +// expected-warning@-2{{redundant same-type constraint 'T' == 'Int'}} \ No newline at end of file From 9f9b81b156df657f1af9f82ea476ddbc1eb82836 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 21 Mar 2022 16:54:05 -0400 Subject: [PATCH 242/242] RequirementMachine: Explicitly specify SmallVector size --- lib/AST/RequirementMachine/ConcreteContraction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/AST/RequirementMachine/ConcreteContraction.cpp b/lib/AST/RequirementMachine/ConcreteContraction.cpp index 0ce15268953d8..a0db2f60e1d88 100644 --- a/lib/AST/RequirementMachine/ConcreteContraction.cpp +++ b/lib/AST/RequirementMachine/ConcreteContraction.cpp @@ -237,7 +237,7 @@ Optional ConcreteContraction::substTypeParameter( // An unresolved DependentMemberType stores an identifier. Handle this // by performing a name lookup into the base type. - SmallVector concreteDecls; + SmallVector concreteDecls; lookupConcreteNestedType(*substBaseType, memberType->getName(), concreteDecls); auto *typeDecl = findBestConcreteNestedType(concreteDecls);