Skip to content

[TypeChecker] Allow inference from default expressions in certain scenarios (under a flag) #41436

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

Merged
merged 14 commits into from
Feb 24, 2022

Conversation

xedin
Copy link
Contributor

@xedin xedin commented Feb 17, 2022

In some circumstances generic argument(s) could only be inferred from a parameter that has a
default expression associated with it, allow type-checker to infer types based on the default
expression(s), for example:

func test(v: T = 42.0) { ... }

Callers of test are exempt from passing a value of v to resolve a type of T, generic argument
would get inferred as Int for empty argument list.

The same applies to generic parameters in nested positions i.e. func test<T>(v: [T] = [...])
or multiple generic parameters: func test<K, V>(_: [K: V] = ....)

The inference is not allowed when:

  • Generic parameter is used in multiple locations in a parameter list (because it would be possible to
    resolve it unambiguously via supplying explicit argument), for example:
    • test<T>(_: T, _: [T] = ...);
    • Generic parameter could be inferred through its relationship to other parameters via same-type
      generic requirements, for example:
    • func test<T: P, U>(_: T, _: U = ...) where U == T.X

It's allowed to specify generic conformance requirements, layout and supertype constraints if
they do not involve other generic parameters.

The behavior is gated under -enable-experimental-type-inference-from-defaults frontend flag.

Resolves: rdar://86033180

@xedin xedin requested a review from hborla February 17, 2022 20:02
@hborla hborla requested a review from slavapestov February 18, 2022 17:34
xedin added 14 commits February 21, 2022 09:59
This type is going to be use to infer generic parameters at the
call site.
…to a method

It's a convenient way to use existing logic for default argument inference
because suhc inference cannot open whole signature but only conformance
and layout constraints associated generic parameters used in a particular
parameter position.
…from defaults

Contextual type could be a type variable or a type with type variables
that have a set of generic requirements, so affected declaration is
the owner of the generic parameter.
`typeCheckParameterDefault` needs access to `applySolution`
in order to apply a solution found in `inference from defaults` mode.
…nt from parameter

Use `getTypeOfDefaultExpr()` to fetch a type associated with default expression
if any. This could only happen in situations where it's acceptable to infer type
of generic parameter(s) associated with parameter from default expression.
…e nested generic parameters

Adds support for parameter types like `[T?]` or `[(T, U?)]`,
and relaxes restriction on same-type generic parameters.

A same-type requirement is acceptable if it only includes
in-scope generic parameters and concrete types i.e. `T.X == Int`
if accepted if `T` is referenced only by a parameter default
expression is being applied to.
…ferred from result as well

This enables support for inference in initializers of generic types
and in `case`s of generic enums e.g.

```
struct S<T> {
  init(_: T = 42) {}
}

enum E<T: Collection> {
case test(_: T = [42])
}
```
@xedin xedin force-pushed the allow-specialization-from-default-expr branch from e7b4d91 to eaa737c Compare February 21, 2022 18:00
@xedin
Copy link
Contributor Author

xedin commented Feb 21, 2022

@swift-ci please build toolchain

1 similar comment
@xedin
Copy link
Contributor Author

xedin commented Feb 21, 2022

@swift-ci please build toolchain

Copy link
Member

@hborla hborla left a comment

Choose a reason for hiding this comment

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

LGTM! Left a few minor thoughts inline.

/// Computes the type of the default expression for a given parameter.
class DefaultArgumentTypeRequest
: public SimpleRequest<DefaultArgumentTypeRequest, Type(ParamDecl *),
RequestFlags::SeparatelyCached> {
Copy link
Member

Choose a reason for hiding this comment

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

Out of curiosity, what's the reason for separately caching the result?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It allows to cache the type during deserialization without having to run the request.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, you can still do that with evaluator.cacheOutput. Not saying you need to change this though

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, I know about that one, just seemed like a consistent thing to do since I'm not really sure which one is better and/or preferred here.


if (Type type = evaluateOrDefault(
ctx.evaluator,
DefaultArgumentTypeRequest{const_cast<ParamDecl *>(this)}, nullptr)) {
Copy link
Member

Choose a reason for hiding this comment

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

Here, can we use Type() as the default value instead of nullptr and return the result of evaluateOrDefault directly?

Copy link
Contributor

Choose a reason for hiding this comment

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

Type() is in fact nullptr (or rather a TypeBase *nullptr wrapped in a 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.

Either way is fine with me

@xedin
Copy link
Contributor Author

xedin commented Feb 23, 2022

@swift-ci please build toolchain

@xedin
Copy link
Contributor Author

xedin commented Feb 23, 2022

@swift-ci please smoke test

@xedin
Copy link
Contributor Author

xedin commented Feb 23, 2022

@swift-ci please smoke test macOS platform

@xedin xedin marked this pull request as ready for review February 24, 2022 08:52
@xedin xedin merged commit 8cfdb99 into swiftlang:main Feb 24, 2022
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.

3 participants