i’ve got a truly overgrown piece of MongoDB SQL that looks like:
$0[.pipeline] = .init
{
$0.stage
{
$0[.match] = .init
{
$0[.expr] = .expr
{
$0[.and] = .init
{
$0.expr
{
$0[.eq] = ("$\(Record[.scope])", id)
}
$0.expr
{
$0[.or] = .init
{
$0.expr
{
$0[.and] = .init
{
$0.expr
{
...
this thing takes about 25s to typecheck, a number i got by timing the build with and without the query body.
when i make a tiny addition:
$0[.pipeline] = .init
{
$0.stage
{
$0[.match] = Mongo.PredicateDocument.init
+++ ^~~~~~~~~~~~~~~~~~~~~~~
{
it takes about 3s to typecheck.
i have seen this problem before with generic and heavily overloaded subscripts, but in this case, the subscript called in $0[.match] is a plain old non-generic subscript with no overloads.
extension Mongo.PipelineStage
{
@inlinable public
subscript(key:Match) -> Mongo.PredicateDocument?
{
get
{
nil
}
set(value)
{
self.bson.push(key, value)
}
}
}
why doesn’t it just “know” that the .init(with:) should be a Mongo.PredicateDocument.init(with:)?
tera
2
If you rename "init(with:)" to a less common "init(withXYZ:)" is it still slow?
the init(with:) actually comes from a protocol MongoDocumentDSL:
extension Mongo
{
/// Not to be confused with ``FilterDocument``.
@frozen public
struct PredicateDocument:MongoDocumentDSL, Sendable
{
public
var bson:BSON.Document
@inlinable public
init(_ bson:BSON.Document)
{
self.bson = bson
}
}
}
/// A `MongoDocumentDSL` is nothing more than a type that supports an
/// ``init(with:)`` builder API.
///
/// The specific encoding API vended and encodability protocol used is up to
/// the conforming type.
public
protocol MongoDocumentDSL:BSONRepresentable<BSON.Document>,
BSONDecodable,
BSONEncodable
{
}
extension MongoDocumentDSL
{
@inlinable public
init()
{
self.init(.init())
}
@inlinable public
init(with populate:(inout Self) throws -> ()) rethrows
{
self.init()
try populate(&self)
}
}
there are a handful of other types that have init(with:)s, but the only ones that exist on Optional are:
// These APIs must additionally be extensions on ``Optional`` and not just
// ``BSONEncodable`` because SE-299 does not support protocol extension
// member lookup with unnamed closure parameters. Only the APIs that take
// closure arguments need to be duplicated here.
extension BSON.Document?
{
@inlinable public
init<CodingKey>(_:CodingKey.Type = CodingKey.self,
with populate:(inout BSON.DocumentEncoder<CodingKey>) throws -> ()) rethrows
{
self = .some(try .init(with: populate))
}
@inlinable public
init(with populate:(inout BSON.DocumentEncoder<BSON.Key>) throws -> ()) rethrows
{
self = .some(try .init(with: populate))
}
}
extension BSON.List?
{
@inlinable public
init(with populate:(inout BSON.ListEncoder) throws -> ()) rethrows
{
self = .some(try .init(with: populate))
}
}
crucially, none of them have Wrapped == Mongo.PredicateDocument, which is the only type that could possible match that subscript.