Skip to content

[WIP] Opaque result types prototype #21137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from

Conversation

jckarter
Copy link
Contributor

@jckarter jckarter commented Dec 8, 2018

WIP towards a prototype of an MVP subset of https://forums.swift.org/t/opaque-result-types/15645 . For an initial implementation, this won't include the resilience, second-order where constraint, or multiple conditional conformance features proposed in the thread.

  • Parsing
  • Decl representation for opaque types
  • Type representation for opaque types (probably as some new kind of ArchetypeType?)
  • Type checking to resolve the underlying type of opaque types
  • Non-resilient SILGen (lowering the opaque type directly to its underlying type)
  • Serialization support

Limitations include:

  • The opaque type is spelled __opaque Protocol or __opaque ProtocolA & ProtocolB [& ProtocolC...] as placeholder spelling.
  • Opaque types can only appear as the return type of func declarations, not properties or closures.
  • where clause constraints on the opaque type are not yet supported, nor are conditional conformances on the opaque type. However, the protocols in the __opaque type can be arbitrary protocols, with any number of associated type or Self type requirements. __opaque types can in turn be inferred as associated types of protocol conformances.
  • The runtime and resilience aspects are not yet implemented. Changing the underlying type of a public opaque type will break ABI.

Some known issues:

  • Because of the missing runtime support, I substitute out opaque types during SILGen in a rather hacky unprincipled way. It's likely that the optimizer, particularly generic specialization, may still cause crashes.
  • The type checker currently crashes if there's a type error in the underlying type, such as if it does not conform to the required protocols.
  • Diagnostics involving opaque types will print the type with a compiler-internal representation involving the original decl name and generic arguments, instead of anything human-understandable.

@jckarter jckarter force-pushed the opaque-type-ast branch 2 times, most recently from 80fed14 to d756010 Compare December 13, 2018 19:13
@jckarter jckarter force-pushed the opaque-type-ast branch 2 times, most recently from 32c9c86 to 58329d2 Compare December 17, 2018 22:46
Copy link
Contributor

@xedin xedin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solver changes look reasonable to me, @jckarter!

@@ -1115,6 +1115,11 @@ ConstraintSystem::solveImpl(Expr *&expr,
// constraint.
if (convertType) {
auto constraintKind = ConstraintKind::Conversion;

if (getContextualTypePurpose() == CTP_ReturnStmt
&& Options.contains(ConstraintSystemFlags::UnderlyingTypeForOpaqueReturnType))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder - do we really need a new flag or we could detect this situation based on convertType itself, because it always would be OpaqueTypeArchetypeType right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we normally want to treat the opaque type and underlying type as distinct outside of return statements.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, what I meant was:

if (getContextualTypePurpose() == CTP_ReturnStmt &&
    covertType->isa<OpaqueTypeArchetypeType>())
  constraintKind = ConstraintKind::OpaqueUnderlyingType;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs for ContextualTypePurpose suggested that it was only intended as a discriminator for diagnostics, so I wasn't sure whether it was OK to key other constraint system behavior solely on it. I can do what you suggested if you think it's better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it was intended like that originally but it would be totally okay to use it for this case as well to avoid adding new flags.

}
}

addConstraint(ConstraintKind::Equal, type1, underlyingTyVar, locator);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for my better understanding, could you please elaborate if this constraint has to be equality, or it could be conversion instead? It seems like in the non-opaque case solver creates a conversion constraint from contextual result to what we infer for return, but in this case I think equality makes more sense because base is going to be a type variable with a set of requirements, is that correct? If so, I wonder if we should just bind these types instead of to pull requirements to the left type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the result of opening the opaque archetype should be a type variable with constraints the value of the return statement would need to satisfy. Are there cases where a Conversion constraint between type variables might still allow a more general set of types to be bound? Bind seems too specific since we still want to allow lvalues in the return statement to be loaded too.

Copy link
Contributor

@xedin xedin Dec 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there cases where a Conversion constraint between type variables might still allow a more general set of types to be bound?

I can only think of implicit conversions being a difference between equals and conversion in that regard.

Bind seems too specific since we still want to allow lvalues in the return statement to be loaded too.

Actually if you look at the matchTypes - both equal and bind share the same path and most likely going to bind types or merge equivalence classes of the type variables if either is a type variable.

@jckarter jckarter force-pushed the opaque-type-ast branch 3 times, most recently from 38e0034 to 65d32b3 Compare January 10, 2019 23:31
@jckarter jckarter force-pushed the opaque-type-ast branch 2 times, most recently from 174ae36 to 2d90612 Compare January 16, 2019 23:48
@jckarter
Copy link
Contributor Author

@swift-ci Please Build Toolchain macOS Platform

@jckarter
Copy link
Contributor Author

@swift-ci Please build toolchain macOS platform

1 similar comment
@jckarter
Copy link
Contributor Author

@swift-ci Please build toolchain macOS platform

@swift-ci
Copy link
Contributor

macOS Toolchain
Download Toolchain
Git Sha - e1c49799b647b49478026e032a19139ae278fa65

Install command
tar -zxf swift-PR-21137-170-osx.tar.gz --directory ~/

Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my review is not very substantiative. But at least I read through most of the code.

@slavapestov
Copy link
Contributor

You have a binary file 'bar' that got added by mistake

@jckarter
Copy link
Contributor Author

@swift-ci Please build toolchain macOS platform

1 similar comment
@jckarter
Copy link
Contributor Author

@swift-ci Please build toolchain macOS platform

@swift-ci
Copy link
Contributor

macOS Toolchain
Download Toolchain
Git Sha - 551d6142287e3d09784e1a692dc5767913248b74

Install command
tar -zxf swift-PR-21137-174-osx.tar.gz --directory ~/

@jckarter
Copy link
Contributor Author

@swift-ci Please build toolchain

To represent the abstracted interface of an opaque type, we need a generic signature that refines
the outer context generic signature with an additional generic parameter representing the underlying
type and its exposed constraints. Opaque types also need to be keyed by their originating decl, so
that we can treat values of the same opaque type as the same. When we check a FuncDecl with an
opaque type specified as its return type, create an OpaqueTypeDecl and associate it with the
originating decl. (A representation for *types* derived from the opaque decl will come next.)
@jckarter
Copy link
Contributor Author

@swift-ci Please build toolchain macOS platform

@swift-ci
Copy link
Contributor

macOS Toolchain
Download Toolchain
Git Sha - b63ccda

Install command
tar -zxf swift-PR-21137-216-osx.tar.gz --directory ~/

@jckarter
Copy link
Contributor Author

The real implementation
#22072 can do everything this can now.

@jckarter jckarter closed this Mar 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants