Trivially nested structs never finish compiling?

I've just discovered an interesting case that may look weird at a first glance, but I think is fairly common in real-world applications. Deeply nested structs are convenient for describing a full state in a fairly big app. Unfortunately, it seems that at a certain level of nesting the compiler just can't process it in any reasonable time. Consider this snippet:

struct S1 {
  let p1: String
  let p2: String
  let p3: String
  let p4: String
  let p5: String
  let p6: String
  let p7: String
  let p8: String
  let p9: String
  let p0: String
}

struct S2 {
  let p1: S1
  let p2: S1
  let p3: S1
  let p4: S1
  let p5: S1
  let p6: S1
  let p7: S1
  let p8: S1
  let p9: S1
  let p0: S1
}

struct S3 {
  let p1: S2
  let p2: S2
  let p3: S2
  let p4: S2
  let p5: S2
  let p6: S2
  let p7: S2
  let p8: S2
  let p9: S2
  let p0: S2
}

struct S4 {
  let p1: S3
  let p2: S3
  let p3: S3
  let p4: S3
  let p5: S3
  let p6: S3
  let p7: S3
  let p8: S3
  let p9: S3
  let p0: S3
}

struct S5 {
  let p1: S4
  let p2: S4
  let p3: S4
  let p4: S4
  let p5: S4
  let p6: S4
  let p7: S4
  let p8: S4
  let p9: S4
  let p0: S4
}

I waited about 5 minutes for this snippet to be compiled, but the swift process never stopped, while constantly consuming 100% of CPU time. RAM consumption also crawled higher up to about 3 gigs before I killed the process.

Interestingly enough, commenting out struct S5 block makes the issue go away and everything compiles in a couple of seconds with four levels of nesting. Converting all structs to classes also makes the issue go away (even with five levels of nesting). This is reproducible with both Swift 5.3.2 shipped in Xcode 12.3 and Swift 5.4 24th of January 2021 snapshot.

Has anyone seen this problem before? Is this a known issue?

6 Likes

Yep, same issue. lldb-rpc-server consumes 100% cpu unless you kill it.

I filed SR-14136 in the meantime.

1 Like

What's really interesting about this case is that Xcode is able to do code completion just fine:

The issue seems to be in the generated LLVM IR:

# this completes reasonably quick
$ swiftc -emit-sil NestedStructs.swift -o NestedStructs.sil

# this takes some time, but completes too
$ swiftc -emit-ir NestedStructs.swift -o NestedStructs.ll

# check the actual size of emitted products
du NestedStructs.*
1.2G	NestedStructs.ll
 36K	NestedStructs.sil
4.0K	NestedStructs.swift

I assume that producing the actual binary from 1.2 gigs of IR (which is definitely too much for a bunch of structs that aren't even used anywhere) is what's taking so long.

If Xcode doesn't emit any IR or binaries when building the code completion index, that's probably expected. Probably not the case for SourceKit LSP with the "indexing while building" approach that relies on the index store produced by a plain swift build invocation.

6 Likes