We need `#fileName`

Yes. With #sourceRoot too, all the use cases I can think of would still be possible. I like that idea much better than the status quo.

1 Like

Without derailing from the main topic, with all these new keywords being pitched I think the context is growing too fast. Would it make sense to generalize that into a special StaticContext<T> type where the generic parameter allows the users to specify the subset of all #keywords, so that the context value isn't exploding in size where not appropriate? This will also reduce the number of parameters a function must have to obtain such static context compared to current status quo.

There was a similar topic, but instead of smashing everything inside one type it would be far better if we could specify a subset of static information we want to obtain:

6 Likes

I agree, that would be a good idea, especially since people are in fact relying on the current behavior of #file and we don't want to disrupt them, so that suggests to me introducing new context values would be preferable to breaking the existing ones.

2 Likes

I really like the idea of StaticContext. It's much more usable than an ad-hoc collection of parameters that all have to be forwarded individually.

5 Likes

Aside the pitch from @davedelong, I tried to create my own type to accumulate the context but I failed because the current static capturing behavior isn't that trivial as I initially thought.

So it definitely requires a formal proposal and some compiler help to create such a type.

IIRC @beccadax has also taken a stab at implementing #context; I do not know how far he's gotten.

Now that __FILE_NAME__ exists in clang for C family languages, it seems like a no brainer to add #fileName for Swift -- same behavior available for all languages.

2 Likes

Which "size" are you concerned about, in particular? The memory of a Context object, or the "mental" size of a large Context API with many properties?

At least in the former case, we should implement #context in such a way that values can be emitted as static global data.

2 Likes

That's what I would have thought.

It would be an issue if all properties were stored, for all uses of #context, even if only a properties are every accessed.

Could there be an optimization to only populate that data which will actually be referenced? Though I imagine an optimization like that would be defeated as soon as someone passes a Context object across a module boundary, or dumps/prints the entire object.

It would be easy to make it so that if you did something like func foo(x: String = #context.file), we only instantiate the file string. Passing around whole context values might be trickier, though in most cases they would probably be passed around as read-only, so if the type is large enough that we pass it indirectly we would just pass the global pointer around.

3 Likes

It would be pretty cool to have a really cheap context like this, especially if it could support Hashable by just comparing / hashing the pointer value.

If it's acceptable to say that context values are only ever compiler-generated, then the type representation could be a plain pointer to the global compiler-generated value. That might cut off possibilities for manipulating context values when forwarding them through different logging APIs, though.

Could that be mitigated by making Context be a reference type? Then compiler generates references would be a pointer to the static information, but as a developer I’d still be able to lash up my own composite/altered contexts.

I'd very much love to be able to carry around source location information as some form of thing "together" very cheaply -- a pointer to a static, compiler generated, immutable "location context" would be quite great. We currently pass around #file and #line, though sometimes I think to myself "function might have been nice to carry here as well" but we don't since it's too much hassle at some point (debate-able, maybe worth it to carry around, maybe not).

Usage patterns we have for these things are usually:

  • carry around the location info always
  • very rarely actually access it -- only when things went wrong, then in asynchronous programs one can say "the thing that originated at line ... failed"
  • this isn't a replacement for full on tracing, but a simpler lightweight source location focused helper

Introducing anything reference type for this hurts the first point, as it'd have to be refcounted I guess; and I can't really afford risking those refcounts in hot code paths (where the carrying is used).

So an immutable compiler generated + direct pointer to it sounds quite great to me -- carrying it around would be cheap, just a pointer after all, and no refcount since the source location info is "immortal" <3


I think this is acceptable for source location; more advanced things I'd expect to go full on tracing (open tracing / dapper / pivot tracing style), and those are runtime generated and kind of expected to "weight" a bit.

1 Like

Don't value types that are bigger than a certain threshold (~40 bytes I think but not declared as it's implementation detail) behave like that under the hood?

I'm sure we could get around that. If there was something like a SourceContext type in the standard library (which only the compiler could construct), the compiler could also know that those objects are always singletons/immortal and omit reference counting.

True my bad actually about the worry about the reference types for this. As long as we know it’s immortal it’s fine indeed.