ITADN

How to treat ambiguous .Self?

#7138Opendanakj 创建于 2026-04-29
leads question
D
danakjcommented
Discussed in [toolchain discussion 2026-04-28](https://docs.google.com/document/d/1mjllGO3ZCL4qGt9uJHUtcxKoHAGEY7Y999ie4EtBWB8/edit?tab=t.h6lecvcwu214#heading=h.twhpk59ki1vo). ## Intro Given a facet, with the following numbered `.Self` facets in its type: ```carbon T:! Z where U impls (Y(.Self) where .Self impls X and .Y1 = C(.Self) and .Y2 = .Self) ^1 ^2 ^3 ^4 ``` Currently the design says that: - `:!` introduces `.Self` of type `type`. - `where` introduces `.Self` with type being the facet type on the LHS of the `where` Normally this just changes the type of `.Self` and they all refer to whatever facet will replace `T`. However that changes in the facet type nested in `U impls <facet type>`. Within that facet type, there are two potential meanings of the `.Self` introduced by a `where`, either `T` or `U`. We call this an ambiguous `.Self`. For discussion, we say `T` is the top-level value for `.Self`, and `U` is the inner-most value for `.Self` in this example. ## What `.Self` does in the toolchain The fundamental purpose of `.Self` is to sit as a placeholder that is replaced later once a self-type is known. We now do this replacement (as of #7097) primarily when identifying a facet type for an associated self-type. This replacement applies to all `.Self` except: - We encode designators in the toolchain as a `ImplWitnessAccess(LookupImplWitness(.Self, Interface), Index)` instruction. The design (not copied out of proposal yet) says designators are [_not_ an implicit `.Self` anymore](https://docs.carbon-lang.dev/proposals/p2173.html#rewrite-constraints), but that they modify the facet/type of the `where` expression they are a part of. This is the inner-most value for `.Self`. So designators in rewrite (and same-type) constraints are _not_ replaced as part of the global replacement with the top-level self-type, and are replaced instead with the inner-most value. - We encode `.Self impls` as a separate constraint type in the toolchain (in `FacetTypeInfo`), which does not include the `.Self` instruction in it at all. So these do not participate in `.Self` replacement, and are instead mapped to a facet/type when handling an `impls` constraint inside `WhereExpr` evaluation. We current map these constraints to be against the current `WhereExpr` being evaluated, which works out the same as saying `.Self` is replaced by the inner-most value. ## What goes wrong with ambiguous `.Self`? The replacement of `.Self` always happens with the top-level value. In this example its `T` with type `Z`. But name lookup finds the nearest `.Self`, which for `^3` is `U` with type `Y(T)`. This creates a problem for replacement if the `.Self` found by name lookup in `^3` is still there in the final facet type when we perform replacement. We will have a value that is replacing `T` and satisfies `Z`, but the `.Self` will want a value for `U` and need it to satisfy `Y(T)`. We eagerly evaluate parts of the facet type if possible. If the example is changed so that `^3` becomes part of an ImplWitnessAccess, such as `C(.Self).C1`, and a value for `.C1` is known to eval, the `.Self` in `^3` can be consumed by eval, resolving to whatever `.C1` is, which may contain either the inner-most _or_ the top-level `.Self`. For instance with this definition of `C`, the result of `.C1` is the top-level value for `.Self`: ```carbon class C[U:! type](T:! Y(U)) { let C1:! type = U; } ``` But with this definition `C`, the result of `.C1` is the inner-most value for `.Self`: ```carbon class C(T:! type) { let C1:! type = T; } ``` The actual symbolic facet `.Self` does not contain any information to differentiate between these two values, so it can change its value depending on changes to `C`, which would be bad. So we consider it ambiguous and reject it `^3`. ## .Self impls As mentioned `.Self impls` is implemented separately in the toolchain, without retaining the `.Self` instruction at all. This is because the `.Self` there sits on its own, so it acts more like a keyword. In particular, it can not resolve in eval to a facet with an ambiguous `.Self`; it is fixed and evaluates to itself. ## Options ### 1. Allow `.Self` impls Allow `^1` and `^2`. Reject `^3` and `^4`. - Always allow `.Self impls` since we can disambiguate it. Say that `.Self` refers to the inner-most value so that it's modifying the `where` that it is a part of. This makes `.Self impls` apply the most similar to rewrites (`.A = B`). - Disallow ambiguous `.Self`, which is any other use of `.Self` in an ambiguous position. ### 1b. Allow `.Self impls`, as top-level Allow `^1` and `^2`. Reject `^3` and `^4` - Always allow `.Self impls` since we can disambiguate it. Say that `.Self` always refers to the top-level value, so `^2` modifies `T` instead of `U` in the original example. In particular, `where .Self impls X and .Y1 = C(.Self)` the `.Self` modifies `T` but the `.Y1` modifies `U`. This makes `.Self impls` apply very differently than rewrites (`.A = B`). - This would require us to maintain the `.Self` instruction in the requirements, instead of erasing it in the FacetTypeInfo. - Disallow ambiguous `.Self`, which is any other use of `.Self` in an ambiguous position. In particular, `.Self impls` does not modify the current `where` expression, it modifies whatever becomes the top-level value. This may make `.Self impls` ambiguous in the presence of composing facet types. The `.Self impls` could be modifying a facet/type that is not part of the current facet type at all. ### 2. Maximal disallowance Allow `^1`. Reject `^2`, `^3` and `^4` - Disallow all ambiguous `.Self`. Don't try to disambiguate `.Self impls`. This makes `.Self` the most self-consistent, but requires users to take more care when writing `impls` constraints. It makes `.Self impls` not work in some places where rewrites (`.A = B`) do work. ### 3. Maximal non-ambiguous allowance Allow `^1`, `^2`, and `^4`. Reject `^3`. - Allow and disambiguate all `.Self` that are used on their own - not as part of some larger type expression. This allows `.Self impls` but also allows `.A = .Self`. But not `.A = C(.Self)`. Disambiguate these `.Self` to be the inner-most value so they modify the same facet/type as rewrites (`.A = B`). - Also allow `.Self` as the self-type for a member access, such as `.Self.A`, as it can always be replaced with the inner-most value immediately without depending on evaluation to erase the `.Self` reference. - Disallow any other ambiguous `.Self`. This makes the error for `C(.Self)` need a bit more explanation, but it allows more use of `.Self`. ### 3b. Maximal non-ambiguous allowance, as top-level Allow `^1`, `^2`, and `^4`. Reject `^3`. Same as 3 but disambiguate `.Self` to always be the top-level value. Same tradeoffs as 1b. Note that `.Y2 = .Y4` in a nested facet type would have the LHS refer to the inner-most value but the RHS would refer to the top-level value. ### 4. Yolo (Inner-most only) Allow `^1`, `^2`, `^3` and `^4`. - Let `.Self` be ambiguous. Name lookup gives you a `.Self` that refers to the inner-most value. Rely on typecheck errors to tell the user what went wrong. The user will need to track multiple `.Self` values and understand what is being eagerly evaluated to follow what facets end up being used for any type expression that involves an ambiguous `.Self`. ### 5. Top-level only Allow `^1`, `^2`, `^3` and `^4`. - `.Self` always refers to the top-level value. Its type is not modified by `where`. We rely on implicit constraints to typecheck use of `.Self` as a non-`type` facet type. - Users have to write `U` to refer to the inner-most value instead of writing `.Self`. - Like 1b, `.Self impls` does not modify the current `where` expression, it modifies whatever becomes the top-level value. This may make `.Self` generally and `.Self impls` ambiguous in the presence of composing facet types. The `.Self impls` could be modifying a facet/type that is not part of the current facet type at all. - Like 3b, `.Y2 = .Y4` in a nested facet type would have the LHS refer to the inner-most value but the RHS would refer to the top-level value.
4 条评论