Skip to content

[Concurrency] Import Objective-C methods with completion handlers as async #33674

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 3 commits into from
Aug 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ namespace swift {
class Type;
class Expr;
class DeclRefExpr;
class ForeignAsyncConvention;
class ForeignErrorConvention;
class LiteralExpr;
class BraceStmt;
Expand Down Expand Up @@ -6148,7 +6149,15 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
/// being dropped altogether. `None` is returned for a normal function
/// or method.
Optional<int> getForeignFunctionAsMethodSelfParameterIndex() const;


/// Set information about the foreign async convention used by this
/// declaration.
void setForeignAsyncConvention(const ForeignAsyncConvention &convention);

/// Get information about the foreign async convention used by this
/// declaration, given that it is @objc and 'async'.
Optional<ForeignAsyncConvention> getForeignAsyncConvention() const;

static bool classof(const Decl *D) {
return D->getKind() >= DeclKind::First_AbstractFunctionDecl &&
D->getKind() <= DeclKind::Last_AbstractFunctionDecl;
Expand Down Expand Up @@ -6277,7 +6286,8 @@ class FuncDecl : public AbstractFunctionDecl {
DeclContext *Parent);

static FuncDecl *createImported(ASTContext &Context, SourceLoc FuncLoc,
DeclName Name, SourceLoc NameLoc, bool Throws,
DeclName Name, SourceLoc NameLoc,
bool Async, bool Throws,
ParameterList *BodyParams, Type FnRetType,
DeclContext *Parent, ClangNode ClangN);

Expand Down
81 changes: 81 additions & 0 deletions include/swift/AST/ForeignAsyncConvention.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===--- ForeignAsyncConvention.h - Async conventions -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the ForeignAsyncConvention structure, which
// describes the rules for how to detect that a foreign API is asynchronous.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_FOREIGN_ASYNC_CONVENTION_H
#define SWIFT_FOREIGN_ASYNC_CONVENTION_H

#include "swift/AST/Type.h"

namespace swift {

/// A small structure describing the async convention of a foreign declaration.
class ForeignAsyncConvention {
/// The index of the completion handler parameters.
unsigned CompletionHandlerParamIndex;

/// When non-zero, indicates which parameter to the completion handler is the
/// Error? parameter (minus one) that makes this async function also throwing.
unsigned CompletionHandlerErrorParamIndex;
public:
ForeignAsyncConvention()
: CompletionHandlerParamIndex(0), CompletionHandlerErrorParamIndex(0) { }

ForeignAsyncConvention(unsigned completionHandlerParamIndex,
Optional<unsigned> completionHandlerErrorParamIndex)
: CompletionHandlerParamIndex(completionHandlerParamIndex),
CompletionHandlerErrorParamIndex(
completionHandlerErrorParamIndex
? *completionHandlerErrorParamIndex + 1
: 0) {}

/// Retrieve the index of the completion handler parameter, which will be
/// erased from the Swift signature of the imported async function.
unsigned completionHandlerParamIndex() const {
return CompletionHandlerParamIndex;
}

/// Retrieve the index of the \c Error? parameter in the completion handler's
/// parameter list. When argument passed to this parameter is non-null, the
/// provided error will be thrown by the async function.
Optional<unsigned> completionHandlerErrorParamIndex() const {
if (CompletionHandlerErrorParamIndex == 0)
return None;

return CompletionHandlerErrorParamIndex - 1;
}

/// Whether the async function is throwing due to the completion handler
/// having an \c Error? parameter.
///
/// Equivalent to \c static_cast<bool>(completionHandlerErrorParamIndex()).
bool isThrowing() const {
return CompletionHandlerErrorParamIndex != 0;
}

bool operator==(ForeignAsyncConvention other) const {
return CompletionHandlerParamIndex == other.CompletionHandlerParamIndex
&& CompletionHandlerErrorParamIndex ==
other.CompletionHandlerErrorParamIndex;
}
bool operator!=(ForeignAsyncConvention other) const {
return !(*this == other);
}
};

}

#endif
23 changes: 20 additions & 3 deletions include/swift/AST/ForeignErrorConvention.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ class ForeignErrorConvention {
}

Info() = default;

Kind getKind() const {
return static_cast<Kind>(TheKind);
}
};

private:
Expand Down Expand Up @@ -178,11 +182,24 @@ class ForeignErrorConvention {
/// Returns the physical result type of the function, for functions
/// that completely erase this information.
CanType getResultType() const {
assert(getKind() == ZeroResult ||
getKind() == NonZeroResult);
assert(resultTypeErasedToVoid(getKind()));
return ResultType;
}


/// Whether this kind of error import erases the result type to 'Void'.
static bool resultTypeErasedToVoid(Kind kind) {
switch (kind) {
case ZeroResult:
case NonZeroResult:
return true;

case ZeroPreservedResult:
case NilResult:
case NonNilError:
return false;
}
}

bool operator==(ForeignErrorConvention other) const {
return info.TheKind == other.info.TheKind
&& info.ErrorIsOwned == other.info.ErrorIsOwned
Expand Down
23 changes: 23 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "ClangTypeConverter.h"
#include "ForeignRepresentationInfo.h"
#include "SubstitutionMapStorage.h"
#include "swift/AST/ForeignAsyncConvention.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/ConcreteDeclRef.h"
#include "swift/AST/DiagnosticEngine.h"
Expand Down Expand Up @@ -281,6 +282,10 @@ struct ASTContext::Implementation {
llvm::DenseMap<const AbstractFunctionDecl *,
ForeignErrorConvention> ForeignErrorConventions;

/// Map from declarations to foreign async conventions.
llvm::DenseMap<const AbstractFunctionDecl *,
ForeignAsyncConvention> ForeignAsyncConventions;

/// Cache of previously looked-up precedence queries.
AssociativityCacheType AssociativityCache;

Expand Down Expand Up @@ -2238,6 +2243,24 @@ AbstractFunctionDecl::getForeignErrorConvention() const {
return it->second;
}

void AbstractFunctionDecl::setForeignAsyncConvention(
const ForeignAsyncConvention &conv) {
assert(hasAsync() && "setting error convention on non-throwing decl");
auto &conventionsMap = getASTContext().getImpl().ForeignAsyncConventions;
assert(!conventionsMap.count(this) && "error convention already set");
conventionsMap.insert({this, conv});
}

Optional<ForeignAsyncConvention>
AbstractFunctionDecl::getForeignAsyncConvention() const {
if (!hasAsync())
return None;
auto &conventionsMap = getASTContext().getImpl().ForeignAsyncConventions;
auto it = conventionsMap.find(this);
if (it == conventionsMap.end()) return None;
return it->second;
}

Optional<KnownFoundationEntity> swift::getKnownFoundationEntity(StringRef name){
return llvm::StringSwitch<Optional<KnownFoundationEntity>>(name)
#define FOUNDATION_ENTITY(Name) .Case(#Name, KnownFoundationEntity::Name)
Expand Down
5 changes: 3 additions & 2 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7255,13 +7255,14 @@ FuncDecl *FuncDecl::createImplicit(ASTContext &Context,

FuncDecl *FuncDecl::createImported(ASTContext &Context, SourceLoc FuncLoc,
DeclName Name, SourceLoc NameLoc,
bool Throws, ParameterList *BodyParams,
bool Async, bool Throws,
ParameterList *BodyParams,
Type FnRetType, DeclContext *Parent,
ClangNode ClangN) {
assert(ClangN && FnRetType);
auto *const FD = FuncDecl::createImpl(
Context, SourceLoc(), StaticSpellingKind::None, FuncLoc, Name, NameLoc,
/*Async=*/false, SourceLoc(), Throws, SourceLoc(),
Async, SourceLoc(), Throws, SourceLoc(),
/*GenericParams=*/nullptr, Parent, ClangN);
FD->setParameters(BodyParams);
FD->setResultInterfaceType(FnRetType);
Expand Down
3 changes: 1 addition & 2 deletions lib/ClangImporter/ClangAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -714,8 +714,7 @@ bool importer::isUnavailableInSwift(
return false;
}

OptionalTypeKind importer::getParamOptionality(version::Version swiftVersion,
const clang::ParmVarDecl *param,
OptionalTypeKind importer::getParamOptionality(const clang::ParmVarDecl *param,
bool knownNonNull) {
auto &clangCtx = param->getASTContext();

Expand Down
6 changes: 1 addition & 5 deletions lib/ClangImporter/ClangAdapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,15 +161,11 @@ bool isUnavailableInSwift(const clang::Decl *decl, const PlatformAvailability &,

/// Determine the optionality of the given Clang parameter.
///
/// \param swiftLanguageVersion What version of Swift we're using, which affects
/// how optionality is inferred.
///
/// \param param The Clang parameter.
///
/// \param knownNonNull Whether a function- or method-level "nonnull" attribute
/// applies to this parameter.
OptionalTypeKind getParamOptionality(version::Version swiftLanguageVersion,
const clang::ParmVarDecl *param,
OptionalTypeKind getParamOptionality(const clang::ParmVarDecl *param,
bool knownNonNull);
}
}
Expand Down
7 changes: 7 additions & 0 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3681,6 +3681,7 @@ void ClangImporter::Implementation::lookupValue(
clangDecl->getMostRecentDecl();

CurrentVersion.forEachOtherImportNameVersion(
SwiftContext.LangOpts.EnableExperimentalConcurrency,
[&](ImportNameVersion nameVersion) {
if (anyMatching)
return;
Expand All @@ -3690,6 +3691,12 @@ void ClangImporter::Implementation::lookupValue(
if (!newName.getDeclName().matchesRef(name))
return;

// If we asked for an async import and didn't find one, skip this.
// This filters out duplicates.
if (nameVersion.supportsConcurrency() &&
!newName.getAsyncInfo())
return;

const clang::DeclContext *clangDC =
newName.getEffectiveContext().getAsDeclContext();
if (!clangDC || !clangDC->isFileContext())
Expand Down
Loading