diff --git a/stdlib/public/core/StringWordBreaking.swift b/stdlib/public/core/StringWordBreaking.swift index ff641179391c4..7010c11cd94c7 100644 --- a/stdlib/public/core/StringWordBreaking.swift +++ b/stdlib/public/core/StringWordBreaking.swift @@ -446,7 +446,14 @@ extension _StringGuts { (.zwj, _): if y != .format && y != .extend && y != .zwj { state.previousProperty = y - state.previousIndex = state.index + + // If we already have a constraint in flight, then use that as our base + // previous index. Otherwise, use where we're at right now. + if let constraint = state.constraint { + state.previousIndex = constraint.index + } else { + state.previousIndex = state.index + } } return false diff --git a/validation-test/stdlib/StringWordBreaking.swift b/validation-test/stdlib/StringWordBreaking.swift index 972a5e18a68a8..11e166deeefa9 100644 --- a/validation-test/stdlib/StringWordBreaking.swift +++ b/validation-test/stdlib/StringWordBreaking.swift @@ -83,6 +83,39 @@ if #available(SwiftStdlib 5.9, *) { } } +// rdar://116652595 +// +// We were accidently hanging when rounding word indices for some concoctions of +// strings. In particular, where we had a pair of scalars create a constraint +// for the preceeding pair, but the preceeding extend rules were not taking the +// constraint into consideration. +if #available(SwiftStdlib 5.10, *) { + StringWordBreaking.test("word breaking backward extend constraints") { + let strs = ["ๆ—ฅ\u{FE0F}:X ", "๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ\u{FE0F}:X ", "โ›”๏ธ:X ", "โ›”๏ธยทX ", "โ›”๏ธ๏ผšX "] + let strWords = [ + ["ๆ—ฅ\u{FE0F}", ":", "X", " "], + ["๐Ÿ‘จโ€๐Ÿ‘จโ€๐Ÿ‘งโ€๐Ÿ‘ฆ\u{FE0F}", ":", "X", " "], + ["โ›”๏ธ", ":", "X", " "], + ["โ›”๏ธ", "ยท", "X", " "], + ["โ›”๏ธ", "๏ผš", "X", " "] + ] + + for (str, words) in zip(strs, strWords) { + expectEqual( + words, + str._words, + "string: \(String(reflecting: str))" + ) + + expectEqual( + words.reversed(), + str._wordsBackwards, + "string: \(String(reflecting: str))" + ) + } + } +} + // The most simple subclass of NSString that CoreFoundation does not know // about. class NonContiguousNSString : NSString {