|
|
@@ -0,0 +1,143 @@
|
|
|
+# Generic impls access (details 4)
|
|
|
+
|
|
|
+<!--
|
|
|
+Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
|
+Exceptions. See /LICENSE for license information.
|
|
|
+SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
+-->
|
|
|
+
|
|
|
+[Pull request](https://github.com/carbon-language/carbon-lang/pull/931)
|
|
|
+
|
|
|
+<!-- toc -->
|
|
|
+
|
|
|
+## Table of contents
|
|
|
+
|
|
|
+- [Problem](#problem)
|
|
|
+- [Background](#background)
|
|
|
+- [Proposal](#proposal)
|
|
|
+- [Rationale based on Carbon's goals](#rationale-based-on-carbons-goals)
|
|
|
+- [Alternatives considered](#alternatives-considered)
|
|
|
+ - [Private impls for public types](#private-impls-for-public-types)
|
|
|
+ - [Private interfaces in public API files](#private-interfaces-in-public-api-files)
|
|
|
+
|
|
|
+<!-- tocstop -->
|
|
|
+
|
|
|
+## Problem
|
|
|
+
|
|
|
+Should a type be able to declare an `impl` to be private, like it can for its
|
|
|
+data and methods? There are some use cases for this feature, but those use cases
|
|
|
+generally could also be addressed by implementing the interface on a private
|
|
|
+adapter type instead.
|
|
|
+
|
|
|
+## Background
|
|
|
+
|
|
|
+Private impls have been considered but not implemented for Swift in the
|
|
|
+[scoped conformances pitch](https://forums.swift.org/t/scoped-conformances/37159).
|
|
|
+
|
|
|
+## Proposal
|
|
|
+
|
|
|
+This is a proposal to add to
|
|
|
+[this design document on generics details](/docs/design/generics/details.md).
|
|
|
+The additions are:
|
|
|
+
|
|
|
+- to say impls do not allow access control modifiers and are always as public
|
|
|
+ as the names used in the signature of the impl; and
|
|
|
+- to document how to use private adapter types instead.
|
|
|
+
|
|
|
+## Rationale based on Carbon's goals
|
|
|
+
|
|
|
+We decided to make generics coherent as part of accepting proposal
|
|
|
+[#24: generics goals](https://github.com/carbon-language/carbon-lang/pull/24).
|
|
|
+This approach is consistent with broader Carbon goals that Carbon have
|
|
|
+[code that is easy to read, understand, and write](/docs/project/goals.md#code-that-is-easy-to-read-understand-and-write).
|
|
|
+In particular, we favor making code
|
|
|
+[less context sensitive](/docs/project/principles/low_context_sensitivity.md).
|
|
|
+
|
|
|
+## Alternatives considered
|
|
|
+
|
|
|
+### Private impls for public types
|
|
|
+
|
|
|
+We considered supporting private impls for public types. This was motivated by
|
|
|
+using the conversion of a `struct` type to a `class` type to construct class
|
|
|
+values. In the case that the class had private data members, we wanted to
|
|
|
+restrict that conversion so it was only available in the bodies of class members
|
|
|
+or friends of the class. We considered achieving that goal by saying that the
|
|
|
+implementation of the conversion would be private in that case. Allowing impls
|
|
|
+to generally be private, though, led to a number of coherence concerns that the
|
|
|
+same code would behave differently in different files. Our solution was to
|
|
|
+address these conversions using a different approach that only addressed the
|
|
|
+conversion use case:
|
|
|
+
|
|
|
+- Users could not implement `struct` to `class` conversions themselves.
|
|
|
+- The compiler would generate `struct` to `class` conversions for constructing
|
|
|
+ class values itself.
|
|
|
+- Those conversion impls will always be as visible as the class type, and will
|
|
|
+ still be selected using the same rules as other impls.
|
|
|
+- When one of these compiler-generated conversion impls is selected, the
|
|
|
+ compiler would perform an access control check to see if it was allowed.
|
|
|
+
|
|
|
+We believe that other use cases for restricting access to an impl are better
|
|
|
+accomplished by using a private adapter type than supporting private impls.
|
|
|
+
|
|
|
+### Private interfaces in public API files
|
|
|
+
|
|
|
+A private interface may only be implemented by a single library, which gives the
|
|
|
+library full control. We considered and rejected the idea that developers could
|
|
|
+put that interface declaration in an API file to allow it to be referenced in
|
|
|
+named constraints available to users. All impls for that interface would also
|
|
|
+have to be declared in the API file, unless they were for a private type
|
|
|
+declared in that library.
|
|
|
+
|
|
|
+This would allow you to express things like:
|
|
|
+
|
|
|
+- A named constraint that is satisfied for any type **not** implementing
|
|
|
+ interface `Foo`:
|
|
|
+
|
|
|
+ ```
|
|
|
+ class TrueType {}
|
|
|
+ class FalseType {}
|
|
|
+ private interface NotFooImpl {
|
|
|
+ let IsFoo:! Type;
|
|
|
+ }
|
|
|
+ impl [T:! Foo] T as NotFooImpl {
|
|
|
+ let IsFoo:! Type = TrueType;
|
|
|
+ }
|
|
|
+ impl [T:! Type] T as NotFooImpl {
|
|
|
+ let IsFoo:! Type = FalseType;
|
|
|
+ }
|
|
|
+ constraint NotFoo {
|
|
|
+ extends NotFooImpl where .IsFoo == FalseType;
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+- A named constraint that ensures `CommonType` is implemented symmetrically:
|
|
|
+
|
|
|
+ ```
|
|
|
+ // For users to implement for types
|
|
|
+ interface CommonTypeWith(U:! Type) {
|
|
|
+ let Result:! Type where ...;
|
|
|
+ }
|
|
|
+
|
|
|
+ // internal library detail
|
|
|
+ private interface AutoCommonTypeWith(U:! Type) {
|
|
|
+ let Result:! Type where ...;
|
|
|
+ }
|
|
|
+
|
|
|
+ // To use as the requirement for parameters
|
|
|
+ constraint CommonType(U:! AutoCommonTypeWith(Self)) {
|
|
|
+ extends AutoCommonTypeWith(U) where .Result == U.Result;
|
|
|
+ }
|
|
|
+
|
|
|
+ match_first {
|
|
|
+ impl [T:! Type, U:! ManualCommonTypeWith(T)]
|
|
|
+ T as AutoCommonTypeWith(U) {
|
|
|
+ let Result:! auto = U.Result;
|
|
|
+ }
|
|
|
+ impl [T:! Type, U:! ManualCommonTypeWith(T)]
|
|
|
+ U as AutoCommonTypeWith(T) {
|
|
|
+ let Result:! auto = U.Result;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ```
|
|
|
+
|
|
|
+This feature is something we might consider adding in the future.
|