Skip to content

[Function builders] Infer function builder from a protocol requirement. #32005

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 5 commits into from
May 26, 2020

Conversation

DougGregor
Copy link
Member

Allow a protocol requirement for a function or property to declare
a function builder. A witness to such a protocol requirement will
infer that function builder when all of the following are two:

  • The witness does not explicitly adopt a function builder
  • The witness is declared within the same context that conforms to the
    protocol requirement (e.g., same extension or primary type declaration)
  • The witness's body does not contain a "return" statement (since those
    disable the function builder transform).

Allow a protocol requirement for a function or property to declare
a function builder. A witness to such a protocol requirement will
infer that function builder when all of the following are two:
* The witness does not explicitly adopt a function builder
* The witness is declared within the same context that conforms to the
protocol requirement (e.g., same extension or primary type declaration)
* The witness's body does not contain a "return" statement (since those
disable the function builder transform).
@DougGregor
Copy link
Member Author

@swift-ci please smoke test

@DougGregor
Copy link
Member Author

@swift-ci please test source compatibility

Now that this request will be triggered more often, due to inference
of function builders, cache the result.
Eliminates both circular references and unwanted dependencies.
@DougGregor
Copy link
Member Author

@swift-ci please smoke test

@DougGregor
Copy link
Member Author

@swift-ci please test source compatibility

@DougGregor
Copy link
Member Author

@swift-ci smoke test macOS

@DougGregor
Copy link
Member Author

Debug source compatibility failure in ACHNBrowserUI is unrelated. It passed in Release, so I think we're fine.

@DougGregor
Copy link
Member Author

What you are thoughts on the situation where closure body is a function body but it has a return just as a force of habit?

I think it's reasonable to have that as a diagnostic path---if there's are explicit returns and we have some sort of type conflict in the returns, in a closure that was matched up against a parameter that has a function builder, try dropping the returns. For this PR, we'd want to recompute the inference path while ignoring the returns to fit in with such a diagnostic.

@xedin
Copy link
Contributor

xedin commented May 26, 2020

To make it easier for diagnostics would it be possible to make return of this function reacher e.g. either type or a failure kind - return in the body, used on getter etc.? applyFunctionBuilder should probably do the same thing...

@DougGregor
Copy link
Member Author

To make it easier for diagnostics would it be possible to make return of this function reacher e.g. either type or a failure kind - return in the body, used on getter etc.? applyFunctionBuilder should probably do the same thing

When we do that diagnostic, we'll want to go through inferFunctionBuilderType again with an "ignore explicit returns" bit on it. Checking for explicit returns early avoids a bunch of name lookups and conformance resolutions that we don't want all the time (because they add incremental dependencies).

@xedin
Copy link
Contributor

xedin commented May 26, 2020

I asking because instead of assuming that ‘return`s could be a problem we can get an indication from this method what exactly went from and act on that when trying to call the method again for diagnostics...

@DougGregor
Copy link
Member Author

I asking because instead of assuming that ‘return`s could be a problem we can get an indication from this method what exactly went from and act on that when trying to call the method again for diagnostics...

If we need more than what I'm suggesting, we can extend the return type. We shouldn't do it proactively because (1) we want to minimize dependencies when not in recovery mode, and (2) we might not need the extra information until we have that diagnostic.

@xedin
Copy link
Contributor

xedin commented May 26, 2020

I see that there is a new diagnostic about adding explicit return added by this PR, why not add a reverse case as well and see what needs to be done to support that?

@DougGregor
Copy link
Member Author

I see that there is a new diagnostic about adding explicit return added by this PR, why not add a reverse case as well and see what needs to be done to support that?

Happy to look at a general improvement for function builders diagnostics to try to address this failure case (an explicit return unexpectedly disabling the function builder transform), but that should be separate. It's a good improvement independent of this PR, and doesn't get significantly harder with this PR.

@xedin
Copy link
Contributor

xedin commented May 26, 2020

Sure! It would be great to address that sooner than later though because not having a good diagnostic there is very confusing.

@DougGregor
Copy link
Member Author

@swift-ci please smoke test

1 similar comment
@DougGregor
Copy link
Member Author

@swift-ci please smoke test

@DougGregor
Copy link
Member Author

@swift-ci smoke test Linux

// Fast path: most declarations (especially parameters, which is where
// this is hottest) do not have any custom attributes at all.
if (!getAttrs().hasAttribute<CustomAttr>()) return nullptr;

Copy link
Contributor

Choose a reason for hiding this comment

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

Losing these hot paths seems abstractly unfortunate. I never profiled them, of course, so who knows, but this does get called for every parameter of every direct call, at least when the argument is a closure. Have you measured compile time/memory impact?

You're only inferring function builders for method bodies, I think, which should let us more selectively weaken the fast-path check.

Copy link
Member Author

Choose a reason for hiding this comment

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

The request-evaluator needs to be fast enough so that we don't need to scatter hot paths like this everywhere, because they suppress incremental dependency generation.

Copy link
Contributor

@rjmccall rjmccall May 26, 2020

Choose a reason for hiding this comment

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

I feel like the statement "[it just] needs to be fast enough" is doing a lot of work for something that inherently looks up and permanently allocates a hash-table entry.

@@ -5038,10 +5038,11 @@ diagnoseMissingAppendInterpolationMethod(NominalTypeDecl *typeDecl) {
}
}

SmallVector<ProtocolConformance *, 2>
std::vector<ProtocolConformance *>
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this use the typedef if you're relying on equivalence?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, it should. Good call.

diag::function_builder_infer_ambig, lookupDecl->getName(),
functionBuilderType, otherFunctionBuilderType);
decl->diagnose(diag::function_builder_infer_add_return)
.fixItInsert(funcDecl->getBodySourceRange().End, "return <#expr#>\n");
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this can't have official source-compatibility impact given that we haven't standardized the feature.

It seems unfortunate that this gets diagnosed here eagerly. I would not expect calling getFunctionBuilderType() to be an authoritative statement by the caller that it wants to try to use the function-builder transform on a method body, and I don't think it's reasonable to diagnose in other cases.

Copy link
Member Author

Choose a reason for hiding this comment

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

You would prefer that we return the (ambiguous) set of possible function builders rather than diagnose here?

Copy link
Contributor

Choose a reason for hiding this comment

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

That might be a more reasonable design, yeah. Or if you really need this behavior, consider renaming the function in a way that makes it clearer that it's not "logically pure" on otherwise-valid code — it triggers query-specific diagnostics if there's no unambiguous function builder type.

@DougGregor
Copy link
Member Author

I'll do some cleanup after this PR for stuff that still needs it.

@DougGregor DougGregor merged commit 8e0ec60 into swiftlang:master May 26, 2020
@DougGregor DougGregor deleted the function-builder-infer branch May 26, 2020 17:59
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.

4 participants