Lỗi trình biên dịch Swift: Biểu hiện quá phức tạp trên một chuỗi nối


143

Tôi thấy điều này thú vị hơn bất cứ điều gì. Tôi đã sửa nó, nhưng tôi tự hỏi về nguyên nhân. Đây là lỗi : DataManager.swift:51:90: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions. Tại sao nó lại phàn nàn? Có vẻ như một trong những biểu hiện đơn giản nhất có thể.

Trình biên dịch trỏ đến columns + ");";phần

func tableName() -> String { return("users"); } 

func createTableStatement(schema: [String]) -> String {

    var schema = schema;

    schema.append("id string");
    schema.append("created integer");
    schema.append("updated integer");
    schema.append("model blob");

    var columns: String = ",".join(schema);

    var statement = "create table if not exists " + self.tableName() + "(" + columns + ");";

    return(statement);
}

cách khắc phục là:

var statement = "create table if not exists " + self.tableName();
statement += "(" + columns + ");";

điều này cũng hoạt động (thông qua @efischency) nhưng tôi không thích nó nhiều vì tôi nghĩ rằng việc (bị lạc:

var statement = "create table if not exists \(self.tableName()) (\(columns))"


10
Bạn đã thấy nếu điều này làm việc : var statement = "create table if not exists \(self.tableName()) (\(columns))"?
quả

5
Nội suy chuỗi, theo khuyến nghị của @efischency, nói chung là một lựa chọn tốt hơn so với nối thủ công với +.
mattt

5
Chắc chắn, nhưng đó không phải là vấn đề. Tôi không quan tâm đó có phải là cách "được đề xuất" hay không, tôi chỉ muốn biết tại sao trình biên dịch lại bóp nghẹt nó. Tôi có một giải pháp hiệu quả, đó không phải là sửa lỗi, mà là tìm hiểu lỗi.
Kendrick Taylor

2
Từ những gì tôi đã nghe, trình biên dịch Swift vẫn đang trong quá trình hoàn thiện. Nhóm nghiên cứu có thể đánh giá cao một báo cáo lỗi về điều này.
molbdnilo

Tôi không có vấn đề biên dịch này với 6.3.1. Tôi đã có những tin nhắn vô lý tương tự trong quá khứ. Chúng ta cần đợi cho đến khi Swift rời khỏi trạng thái alpha.
qwerty_so

Câu trả lời:


183

Tôi không phải là chuyên gia về trình biên dịch - Tôi không biết câu trả lời này sẽ "thay đổi cách bạn nghĩ theo cách có ý nghĩa", nhưng sự hiểu biết của tôi về vấn đề này là:

Nó phải làm với kiểu suy luận. Mỗi lần bạn sử dụng +toán tử, Swift phải tìm kiếm trong tất cả các tình trạng quá tải có thể xảy +ra và suy ra phiên bản +nào bạn đang sử dụng. Tôi đếm chỉ dưới 30 quá tải cho các +nhà điều hành. Đó là rất nhiều khả năng, và khi bạn kết hợp 4 hoặc 5 +thao tác lại với nhau và yêu cầu trình biên dịch suy ra tất cả các đối số, bạn sẽ hỏi nhiều hơn so với cái nhìn đầu tiên.

Suy luận đó có thể trở nên phức tạp - ví dụ: nếu bạn thêm UInt8Intsử dụng +, đầu ra sẽ là một Int, nhưng có một số công việc đánh giá các quy tắc để trộn các loại với toán tử.

Và khi bạn đang sử dụng chữ, như Stringchữ trong ví dụ của bạn, trình biên dịch thực hiện công việc chuyển đổi Stringchữ thành a String, và sau đó thực hiện công việc suy ra các kiểu đối số và trả về cho +toán tử, v.v.

Nếu một biểu thức đủ phức tạp - nghĩa là, nó yêu cầu trình biên dịch đưa ra quá nhiều suy luận về các đối số và toán tử - nó thoát ra và cho bạn biết rằng nó thoát.

Có trình biên dịch thoát khi một biểu thức đạt đến một mức độ phức tạp nhất định là có chủ ý. Cách khác là để cho trình biên dịch thử và thực hiện nó, và xem liệu nó có thể không, nhưng đó là rủi ro - trình biên dịch có thể tiếp tục cố gắng mãi mãi, sa lầy hoặc chỉ bị sập. Vì vậy, sự hiểu biết của tôi là có một ngưỡng tĩnh cho sự phức tạp của một biểu thức mà trình biên dịch sẽ không vượt quá.

Tôi hiểu rằng nhóm Swift đang làm việc trên các tối ưu hóa trình biên dịch sẽ làm cho các lỗi này ít phổ biến hơn. Bạn có thể tìm hiểu một chút về nó trên các diễn đàn Nhà phát triển của Apple bằng cách nhấp vào liên kết này .

Trên các diễn đàn Dev, Chris Lattner đã yêu cầu mọi người nộp các lỗi này dưới dạng báo cáo về radar, bởi vì họ đang tích cực sửa chữa chúng.

Đó là cách tôi hiểu nó sau khi đọc một số bài đăng ở đây và trên diễn đàn Dev về nó, nhưng sự hiểu biết của tôi về trình biên dịch là ngây thơ, và tôi hy vọng rằng ai đó có kiến ​​thức sâu hơn về cách họ xử lý các tác vụ này sẽ mở rộng về những gì tôi đã viết ở đây.


Tôi đã tìm ra một cái gì đó cho hiệu ứng đó, nhưng đó là một câu trả lời hữu ích không hơn không kém. Cảm ơn đã trả lời. Bạn đã đếm số lượng toán tử + bằng tay hay có một số cách khéo léo mà tôi không biết?
Kendrick Taylor

Tôi chỉ xem qua nó trên SwiftDoc.org và đếm chúng bằng tay. Đây là trang tôi đang nói đến: swiftdoc.org/operator/pls
Aaron Rasmussen

28
Đây là một lỗi, bất kể họ sẽ gọi nó như thế nào. Trình biên dịch ngôn ngữ khác không có vấn đề với mã tương tự như những gì đã được đăng. Đề nghị người dùng cuối nên sửa nó là ngớ ngẩn.
Giăng

7
Kiểu suy luận? Điểm quan trọng của việc có một ngôn ngữ được gõ mạnh như Swift (trong đó bạn thậm chí không thể nối chuỗi String + Int mà không phải sử dụng Int) trong tình huống khó hiểu này? Một lần nữa, Swift cố gắng giải quyết vấn đề không ai có ở nơi đầu tiên.
Azurlake

10
@ John Không phải là một lỗi, chỉ là thiết kế ngôn ngữ xấu nếu bạn hỏi tôi! Swift đi quá xa chỉ cố gắng để khác biệt.
T. Rex

31

Điều này gần giống như câu trả lời được chấp nhận nhưng với một số đoạn hội thoại được thêm vào (tôi đã có với Rob Napier, các câu trả lời khác của anh ấy và Matt, Oliver, David từ Slack) và các liên kết.

Xem các ý kiến ​​trong cuộc thảo luận này . Ý chính của nó là:

+ bị quá tải (Apple dường như đã sửa lỗi này trong một số trường hợp)

Các +nhà điều hành được nhiều quá tải, tính đến bây giờ nó có 27 chức năng khác nhau vì vậy nếu bạn đang concatenating 4 chuỗi tức là bạn có 3 +nhà khai thác trình biên dịch phải kiểm tra giữa 27 nhà khai thác mỗi lần, vì vậy đó là 27 ^ 3 lần. Nhưng đó không phải là nó.

Ngoài ra còn có một kiểm tra để xem nếu lhsrhscủa các +chức năng đều hợp lệ nếu chúng được gọi thông qua lõi để appendgọi. Ở đó bạn có thể thấy có một số kiểm tra chuyên sâu có thể xảy ra. Nếu chuỗi được lưu trữ không liền kề, điều này dường như là trường hợp nếu chuỗi bạn đang xử lý thực sự được kết nối với NSString. Swift sau đó phải lắp ráp lại tất cả các bộ đệm mảng byte thành một bộ đệm liền kề duy nhất và yêu cầu tạo bộ đệm mới trên đường đi. và cuối cùng bạn nhận được một bộ đệm chứa chuỗi bạn đang cố gắng nối với nhau.

Tóm lại, có 3 cụm kiểm tra trình biên dịch sẽ làm bạn chậm lại, tức là mỗi biểu thức phụ phải được xem xét lại trong mọi thứ mà nó có thể trả về . Kết quả là các chuỗi kết hợp với phép nội suy tức là sử dụng " My fullName is \(firstName) \(LastName)"sẽ tốt hơn nhiều so với "My firstName is" + firstName + LastNamephép nội suy không có bất kỳ sự quá tải nào

Swift 3 đã thực hiện một số cải tiến. Để biết thêm thông tin đọc Làm thế nào để hợp nhất nhiều Mảng mà không làm chậm trình biên dịch? . Tuy nhiên, +toán tử vẫn bị quá tải và tốt hơn là sử dụng phép nội suy chuỗi cho chuỗi dài hơn


Sử dụng các tùy chọn (vấn đề đang diễn ra - giải pháp có sẵn)

Trong dự án rất đơn giản này:

import UIKit

class ViewController: UIViewController {

    let p = Person()
    let p2 = Person2()

    func concatenatedOptionals() -> String {
        return (p2.firstName ?? "") + "" + (p2.lastName ?? "") + (p2.status ?? "")
    }

    func interpolationOptionals() -> String {
        return "\(p2.firstName ?? "") \(p2.lastName ?? "")\(p2.status ?? "")"
    }

    func concatenatedNonOptionals() -> String {
        return (p.firstName) + "" + (p.lastName) + (p.status)
    }

    func interpolatedNonOptionals() -> String {
        return "\(p.firstName) \(p.lastName)\(p.status)"
    }
}


struct Person {
    var firstName = "Swift"
    var lastName = "Honey"
    var status = "Married"
}

struct Person2 {
    var firstName: String? = "Swift"
    var lastName: String? = "Honey"
    var status: String? = "Married"
}

Thời gian biên dịch cho các hàm là như sau:

21664.28ms  /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:16:10 instance method concatenatedOptionals()
2.31ms  /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:20:10 instance method interpolationOptionals()
0.96ms  /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:24:10 instance method concatenatedNonOptionals()
0.82ms  /Users/Honey/Documents/Learning/Foundational/CompileTime/CompileTime/ViewController.swift:28:10 instance method interpolatedNonOptionals()

Lưu ý rằng thời gian biên dịch điên rồ cao đến mức nào concatenatedOptionals.

Điều này có thể được giải quyết bằng cách làm:

let emptyString: String = ""
func concatenatedOptionals() -> String {
    return (p2.firstName ?? emptyString) + emptyString + (p2.lastName ?? emptyString) + (p2.status ?? emptyString)
}

mà biên dịch trong 88ms

Nguyên nhân cốt lõi của vấn đề là trình biên dịch không xác định ""là a String. Nó thực sựExpressibleByStringLiteral

Trình biên dịch sẽ thấy ??và sẽ phải lặp qua tất cả các loại đã tuân thủ giao thức này , cho đến khi nó tìm thấy một loại có thể là mặc định String. Bằng cách sử dụng emptyStringmã hóa cứng String, trình biên dịch không còn cần phải lặp qua tất cả các loại tuân thủExpressibleByStringLiteral

Để tìm hiểu làm thế nào để đăng nhập thời gian biên dịch xem tại đây hoặc ở đây


Các câu trả lời tương tự khác của Rob Napier trên SO:

Tại sao bổ sung chuỗi mất quá nhiều thời gian để xây dựng?

Làm cách nào để hợp nhất nhiều Mảng mà không làm chậm trình biên dịch?

Swift Array chứa chức năng khiến thời gian xây dựng dài


19

Điều này khá vô lý cho dù bạn nói gì! :)

nhập mô tả hình ảnh ở đây

Nhưng điều này được thông qua dễ dàng

return "\(year) \(month) \(dayString) \(hour) \(min) \(weekDay)"

2

Tôi đã có vấn đề tương tự:

expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions

Trong dòng Xcode 9.3 như sau:

let media = entities.filter { (entity) -> Bool in

Sau khi thay đổi nó thành một cái gì đó như thế này:

let media = entities.filter { (entity: Entity) -> Bool in

mọi thứ đã làm ra.

Có lẽ nó có liên quan đến trình biên dịch Swift đang cố gắng suy ra kiểu dữ liệu từ mã xung quanh.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.