신속한 컴파일러 오류 : 문자열 연결에서“표현이 너무 복잡합니다”


143

나는 이것이 무엇보다 재미있는 것을 발견합니다. 문제를 해결했지만 원인이 궁금합니다. 오류는 다음과 같습니다 DataManager.swift:51:90: Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions.. 왜 불평합니까? 가능한 가장 간단한 표현 중 하나 인 것 같습니다.

컴파일러는 columns + ");";섹션을 가리 킵니다.

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);
}

수정 사항은 다음과 같습니다.

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

이것은 (@efischency를 통해) 작동하지만 (잃어버린 것으로 생각하기 때문에 좋아하지 않습니다 .

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


10
이것이 작동하는지 보셨습니까 var statement = "create table if not exists \(self.tableName()) (\(columns))"?
efischency

5
@efischency에서 권장하는 문자열 보간은 일반적으로와 수동 연결보다 더 나은 옵션입니다 +.
mattt

5
물론, 요점이 아닙니다. 나는 그것이 "추천 된"방법인지 아닌지 상관하지 않으며, 단지 컴파일러가 왜 질식하는지 알고 싶습니다. 작동하는 해결책이 있습니다. 오류를 수정하는 것이 아니라 오류를 이해하는 것입니다.
Kendrick Taylor

2
내가 들었던 것으로부터 Swift 컴파일러는 여전히 많은 작업을 진행하고 있습니다. 팀은 이것에 대한 버그 보고서에 감사 할 것입니다.
molbdnilo

6.3.1로 이것을 컴파일하는 데 아무런 문제가 없었습니다. 나는 과거에 비슷한 말도 안되는 메시지를 받았다. Swift가 알파 상태를 벗어날 때까지 기다려야합니다.
qwerty_so

답변:


183

나는 컴파일러 전문가가 아닙니다-이 답변이 "의미있는 방식으로 생각하는 방식을 바꿀 것"인지 모르겠지만 문제에 대한 나의 이해는 다음과 같습니다.

형식 유추와 관련이 있습니다. +연산자 를 사용할 때마다 Swift는 가능한 모든 과부하를 검색 하여 사용 +중인 버전을 추론 +해야합니다. +운영자에게 과부하가 30 회 미만인 것으로 계산했습니다 . 그것은 많은 가능성이며, 4 또는 5 개의 +연산을 함께 묶고 컴파일러가 모든 인수를 유추하도록 요청하면 언뜻보기에 더 많은 것을 요구합니다.

이러한 추론은 복잡해질 수 있습니다. 예를 들어 a UInt8Intusing 을 추가 +하면 출력이 Int되지만 유형을 연산자와 혼합하는 규칙을 평가하는 작업이 있습니다.

그리고 String예제에서 리터럴과 같은 리터럴을 사용할 때 컴파일러는 String리터럴을 로 변환 String하는 작업을 수행 한 다음 +연산자 의 인수 및 반환 유형을 유추하는 작업을 수행합니다 .

식이 충분히 복잡한 경우 (즉, 컴파일러가 인수와 연산자에 대해 너무 많은 추론을해야 함) 종료하고 종료한다고 알려줍니다.

식이 특정 수준의 복잡성에 도달하면 컴파일러를 종료하는 것은 의도적 인 것입니다. 대안은 컴파일러가 시도하고 수행하도록하고 가능한지 확인하는 것이지만 위험합니다. 그래서 내 이해는 컴파일러가 넘어 가지 않을 식의 복잡성에 대한 정적 임계 값이 있다는 것입니다.

내 이해는 Swift 팀이 이러한 오류를 덜 일반적으로 만드는 컴파일러 최적화 작업을하고 있다는 것입니다. 이 링크를 클릭하면 Apple 개발자 포럼에서 약간 배울 수 있습니다 .

개발자 포럼에서 Chris Lattner는 이러한 오류를 적극적으로 해결하기 위해이 오류를 레이더 보고서로 제출하도록 요청했습니다.

그것이 여기와 Dev 포럼에서 많은 게시물을 읽은 후에 그것을 이해하는 방법이지만 컴파일러에 대한 나의 이해는 순진하며, 이러한 작업을 처리하는 방법에 대한 더 깊은 지식을 가진 사람이 내가 무엇을 확장 할 수 있기를 바랍니다. 여기에 썼습니다.


나는 그 효과에 뭔가를 생각했지만 도움이되는 답변이었습니다. 대답 해줘서 고마워. + 연산자의 수를 손으로 세었습니까, 아니면 내가 알지 못하는 매끄러운 방법이 있습니까?
켄드릭 테일러

방금 SwiftDoc.org에서 엿보고 손으로 세었습니다. 이 페이지는 내가 말하는 페이지입니다 : swiftdoc.org/operator/pls
Aaron Rasmussen

28
그들이 그것을 호출할지 여부에 관계없이 이것은 버그입니다. 다른 언어의 컴파일러는 게시 된 것과 유사한 코드에 문제가 없습니다. 최종 사용자에게 수정을 제안하는 것은 어리석은 일입니다.
John

7
타입 추론? 이 끔찍한 상황에서 Swift와 같은 강력한 유형의 언어 (Int를 캐스팅하지 않고 String + Int를 연결할 수도 없음)에 대한 요점이 무엇입니까? 다시 한번, Swift는 처음에는 아무도 없었던 문제를 해결하려고 시도합니다.
Azurlake는

10
@ 존 버그가 아니라 나에게 묻는다면 나쁜 언어 디자인이다! 스위프트는 너무 달라서 너무 멀리 가고 있습니다.
T. Rex

31

이것은 허용되는 답변과 거의 동일하지만 대화가 추가되었습니다 (Rob Napier, 그의 다른 답변 및 Matt, Oliver, Slack의 David와의 대화) 및 링크가 있습니다.

토론 의 의견을 참조하십시오 . 그것의 요지는 :

+ 과도하게 과부하되었습니다 (Apple은 경우에 따라이 문제를 해결 한 것으로 보입니다)

+연산자는 당신이 4 문자열 즉 합치되도록 경우 27 개 다른 기능을 가지고 현재로서는 크게는 3 개가, 과부하 +컴파일러가하는 사업자 확인 즉 27 ^ 3 번, 그래서 27 개 사업자마다 사이를. 그러나 그것은 아닙니다.

도있다 체크 있는지 lhsrhs+기능들이는 핵심에 통해 호출하는 경우 모두 유효 append라고는. 거기에서 발생할 수있는 다소 집중적 인 검사 가 많이 있습니다. 문자열이 비 연속적으로 저장되는 경우 다루는 문자열이 실제로 NSString에 브리지 된 경우에 해당됩니다. 그런 다음 Swift는 모든 바이트 배열 버퍼를 하나의 연속 된 버퍼로 다시 어셈블해야하며 그 과정에서 새 버퍼를 만들어야합니다. 그런 다음 결국 연결하려고하는 문자열이 포함 된 하나의 버퍼를 얻습니다.

간단히 말해 3 개의 컴파일러 검사 클러스터가있어 속도가 느려집니다. 즉, 각 하위 표현식을 반환 할 있는 모든 것을 고려하여 다시 고려해야 합니다. 결과적으로 보간으로 문자열을 연결하는 것, 즉 보간에 과부하 가 없기 때문에 사용하는 " My fullName is \(firstName) \(LastName)"것이 훨씬 낫습니다."My firstName is" + firstName + LastName

Swift 3일부 개선되었습니다. 자세한 내용 은 컴파일러 속도를 늦추지 않고 여러 배열을 병합하는 방법을 참조하십시오 . . 그럼에도 불구하고 +연산자는 여전히 과부하 상태이며 더 긴 문자열에는 문자열 보간을 사용하는 것이 좋습니다


옵션 사용 (계속되는 문제-사용 가능한 솔루션)

이 매우 간단한 프로젝트에서 :

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"
}

함수의 컴파일 시간은 다음과 같습니다.

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()

컴파일 지속 시간이 얼마나 큰지 주목하십시오 concatenatedOptionals.

다음을 수행하여 해결할 수 있습니다.

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

컴파일 88ms

문제의 근본 원인은 컴파일러가 식별하지 않는다는 것입니다 ""A와 String. 실제로ExpressibleByStringLiteral

컴파일러는 볼 수 ??와 할 것이다 이 프로토콜을 본 모든 종류의를 통해 루프 가에 기본이 될 수있는 유형을 발견까지, String. emptyString를로 코딩 하여 를 사용 String하면 컴파일러는 더 이상 모든 일치 유형을 반복 할 필요가 없습니다.ExpressibleByStringLiteral

컴파일 시간을 기록하는 방법을 알아 보려면 여기 또는 여기를 참조 하십시오


SO에 Rob Napier의 다른 유사한 답변 :

왜 문자열 추가에 시간이 오래 걸립니까?

컴파일러 속도를 늦추지 않고 여러 배열을 병합하는 방법은 무엇입니까?

스위프트 어레이는 빌드 시간을 길게하는 기능을 포함합니다.


19

당신이 무슨 말을하든 이것은 우스운 일입니다! :)

여기에 이미지 설명을 입력하십시오

그러나 이것은 쉽게 전달됩니다.

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

2

비슷한 문제가있었습니다.

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

Xcode 9.3 줄에서 다음과 같습니다.

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

다음과 같이 변경 한 후 :

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

모든 것이 해결되었습니다.

아마도 Swift 컴파일러와 코드 관련 데이터 유형을 유추하려고하는 것과 관련이 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.