carbon-language / carbon-lang

Carbon Language's main repository: documents, design, implementation, and related tools. (NOTE: Carbon Language is experimental; see README)
http://docs.carbon-lang.dev/
Other
32.28k stars 1.48k forks source link

can type values be used at runtime? #1293

Closed zygoloid closed 1 year ago

zygoloid commented 2 years ago

Presumably we will require a compile-time constant[*] type in all contexts where the language requires a type, such as in the type of a variable or field or function parameter. But absent some kind of restriction, the types-as-values model would presumably also allow types to appear at runtime:

Example:

fn F(T:! Type) -> (Type, i32) {
  return (T*, 5);
}
fn G() {
  match (F(i32)) {
    case (i32*, n: i32) => return 0;
    default => return 1;
  }
}

Note that we might require T:! Type in F, not merely T: Type, because the postfix * operator presumably requires its operand to be a compile-time constant.

We certainly could support code such as the above. We'd need to create values to represent types at runtime (eg, RTTI) that are sufficient to allow runtime pattern-matching on types, plus whatever other operations we support on type values where we don't require a compile-time constant type (of which there may be vanishingly few). We would not need to allow those values to be created at runtime if we restrict all the type creation operations to being compile-time only. We would only need to emit RTTI for types that are actually used at runtime such as in the above code.

Or we could find some kind of restriction that would disallow the above while still allowing the cases that we presumably want to allow. Some of those cases might get pretty close to the above; here are some contrived examples:

// F same as above.
let v:! auto = F(i32);
let Arr:! Type = Array(v[1], v[0]);
let arr: Arr = (1, 2, 3, 4, 5);

and

fn G[template T:!  Type](x: T*) {
  match ((T, x)) {
    case (i32, x: i32*) => { ... }
  }
}

[*] By "compile-time constant", I mean after monomorphization, not during generic type-checking.

chandlerc commented 2 years ago

I would suggest we allow type values to be used at runtime in general.

If we want to restrict RTTI in some ways (which we probably do), I would suggest we do that by restricting the operations allowed on those types rather than trying to restrict the types from being used in runtime expressions at all.

Personally, I would like to allow some minimal operations, and would just like us to carefully design them such that we can implement them efficiently enough to never need to develop dialects where some folks need to disable this like we've seen with RTTI. I think we can do that, will just need careful design. I also don't think this issue is where that design should happen. When it gets to someone's priority, they should just propose to add a runtime operation for types. =]

Does that make sense as a decision here?

zygoloid commented 2 years ago

We don't intend in this issue to give precise rules as to which operations we allow and disallow at runtime, but it seems useful to give some indication of what the leads are thinking, as guidance for people planning on proposals in this area. I suggest the following:

chandlerc commented 1 year ago

Several of us had further discussions here in the open discussion session that led those of us in the discussion (including both myself and @zygoloid) to shift our stance a bit here.

We kept finding challenges and reasonable concerns with having any runtime operation in this space. And there is a quite compelling alternative: that there exist one (or several) template operations on a type that reify it into a runtime value with the desired APIs / operations / etc.

By starting from a known, template-phase operation we get the chance to explicitly have the set of all types used in this way known at compile time in order to be maximally efficient in how much data or other supporting infrastructure is produced.

We also can easily reify into something with the desired expressive API without having to solve some of the complexities of attempting to have such an API on the type value itself.

So I think I'd suggest that we actually say simply "no" to this question for now, and allow a later design to explore and develop the desired template-phase operations and supporting runtime APIs when it rises to someone's priorities (I don't think it is quite there yet).

chandlerc commented 1 year ago

I think this one has reached consensus, and I was supposed to mark it as closed with my last comment when I made that. Sorry for the late update!