Skip to content

Named opaque result types #40715

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 4 commits into from
Jan 5, 2022
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
12 changes: 12 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ namespace swift {

namespace ast_scope {
class AbstractPatternEntryScope;
class GenericParamScope;
class PatternEntryDeclScope;
class PatternEntryInitializerScope;
} // namespace ast_scope
Expand Down Expand Up @@ -1565,6 +1566,7 @@ class PatternBindingEntry {
friend class PatternBindingInitializer;
friend class PatternBindingDecl;
friend class ast_scope::AbstractPatternEntryScope;
friend class ast_scope::GenericParamScope;
friend class ast_scope::PatternEntryDeclScope;
friend class ast_scope::PatternEntryInitializerScope;

Expand Down Expand Up @@ -2779,6 +2781,16 @@ class OpaqueTypeDecl final :
return OpaqueInterfaceGenericSignature.getInnermostGenericParams();
}

/// Whether the generic parameters of this opaque type declaration were
/// explicit, i.e., for named opaque result types.
bool hasExplicitGenericParams() const;

/// When the generic parameters were explicit, returns the generic parameter
/// corresponding to the given ordinal.
///
/// Otherwise, returns \c nullptr.
GenericTypeParamDecl *getExplicitGenericParam(unsigned ordinal) const;

/// Retrieve the buffer containing the opaque return type
/// representations that correspond to the opaque generic parameters.
ArrayRef<OpaqueReturnTypeRepr *> getOpaqueReturnTypeReprs() const {
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4134,6 +4134,9 @@ ERROR(opaque_type_no_underlying_type_candidates,none,
ERROR(opaque_type_mismatched_underlying_type_candidates,none,
"function declares an opaque return type %0, but the return statements "
"in its body do not have matching underlying types", (TypeRepr *))
ERROR(opaque_type_mismatched_underlying_type_candidates_named,none,
"function declares an opaque return type %0, but the return statements "
"in its body do not have matching underlying types", (Identifier))
NOTE(opaque_type_underlying_type_candidate_here,none,
"return statement has underlying type %0", (Type))
ERROR(opaque_type_self_referential_underlying_type,none,
Expand Down
12 changes: 11 additions & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1558,7 +1558,17 @@ unsigned ASTMangler::appendBoundGenericArgs(DeclContext *dc,

// If we are generic at this level, emit all of the replacements at
// this level.
if (genericContext->isGeneric()) {
bool treatAsGeneric;
if (auto opaque = dyn_cast<OpaqueTypeDecl>(decl)) {
// For opaque type declarations, the generic parameters of the opaque
// type declaration are not part of the mangling, so check whether the
// naming declaration has generic parameters.
auto namedGenericContext = opaque->getNamingDecl()->getAsGenericContext();
treatAsGeneric = namedGenericContext && namedGenericContext->isGeneric();
} else {
treatAsGeneric = genericContext->isGeneric();
}
if (treatAsGeneric) {
auto genericParams = subs.getGenericSignature().getGenericParams();
unsigned depth = genericParams[currentGenericParamIdx]->getDepth();
auto replacements = subs.getReplacementTypes();
Expand Down
22 changes: 20 additions & 2 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4338,9 +4338,10 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
case PrintOptions::OpaqueReturnTypePrintingMode::Description:
return true;
case PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword:
return false;
return opaque->getDecl()->hasExplicitGenericParams();
case PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword:
return isSimpleUnderPrintOptions(opaque->getExistentialType());
return opaque->getDecl()->hasExplicitGenericParams() ||
isSimpleUnderPrintOptions(opaque->getExistentialType());
}
llvm_unreachable("bad opaque-return-type printing mode");
}
Expand Down Expand Up @@ -5410,11 +5411,28 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
}

void visitOpaqueTypeArchetypeType(OpaqueTypeArchetypeType *T) {
// Try to print a named opaque type.
auto printNamedOpaque = [&] {
if (auto genericParam =
T->getDecl()->getExplicitGenericParam(T->getOrdinal())) {
visit(genericParam->getDeclaredInterfaceType());
return true;
}

return false;
};

switch (Options.OpaqueReturnTypePrinting) {
case PrintOptions::OpaqueReturnTypePrintingMode::WithOpaqueKeyword:
if (printNamedOpaque())
return;

Printer.printKeyword("some", Options, /*Suffix=*/" ");
LLVM_FALLTHROUGH;
case PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword: {
if (printNamedOpaque())
return;

visit(T->getExistentialType());
return;
}
Expand Down
46 changes: 40 additions & 6 deletions lib/AST/ASTScopeCreation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,26 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint(
// Initializers come before VarDecls, e.g. PCMacro/didSet.swift 19
auto patternEntry = getPatternEntry();

// If the pattern type is for a named opaque result type, introduce the
// generic type parameters based on the first variable we find.
ASTScopeImpl *leaf = this;
auto pattern = patternEntry.getPattern();
if (auto typedPattern = dyn_cast<TypedPattern>(pattern)) {
if (auto namedOpaque =
dyn_cast_or_null<NamedOpaqueReturnTypeRepr>(
typedPattern->getTypeRepr())) {
bool addedOpaqueResultTypeScope = false;
pattern->forEachVariable([&](VarDecl *var) {
if (addedOpaqueResultTypeScope)
return;

leaf = scopeCreator.addNestedGenericParamScopesToTree(
var, namedOpaque->getGenericParams(), leaf);
addedOpaqueResultTypeScope = true;
});
}
}

// Create a child for the initializer, if present.
// Cannot trust the source range given in the ASTScopeImpl for the end of the
// initializer (because of InterpolatedLiteralStrings and EditorPlaceHolders),
Expand All @@ -724,7 +744,7 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint(
"Original inits are always after the '='");
scopeCreator
.constructExpandAndInsert<PatternEntryInitializerScope>(
this, decl, patternEntryIndex);
leaf, decl, patternEntryIndex);
}

// If this pattern binding entry was created by the debugger, it will always
Expand All @@ -741,12 +761,12 @@ PatternEntryDeclScope::expandAScopeThatCreatesANewInsertionPoint(
"inits are always after the '='");
scopeCreator
.constructExpandAndInsert<PatternEntryInitializerScope>(
this, decl, patternEntryIndex);
leaf, decl, patternEntryIndex);
}

// Add accessors for the variables in this pattern.
patternEntry.getPattern()->forEachVariable([&](VarDecl *var) {
scopeCreator.addChildrenForParsedAccessors(var, this);
pattern->forEachVariable([&](VarDecl *var) {
scopeCreator.addChildrenForParsedAccessors(var, leaf);
});

// In local context, the PatternEntryDeclScope becomes the insertion point, so
Expand Down Expand Up @@ -849,6 +869,20 @@ TopLevelCodeScope::expandAScopeThatCreatesANewInsertionPoint(ScopeCreator &

// Create child scopes for every declaration in a body.

namespace {
/// Retrieve the opaque generic parameter list if present, otherwise the normal generic parameter list.
template<typename T>
GenericParamList *getPotentiallyOpaqueGenericParams(T *decl) {
if (auto opaqueRepr = decl->getOpaqueResultTypeRepr()) {
if (auto namedOpaque = dyn_cast<NamedOpaqueReturnTypeRepr>(opaqueRepr)) {
return namedOpaque->getGenericParams();
}
}

return decl->getGenericParams();
}
}

void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
ScopeCreator &scopeCreator) {
scopeCreator.addChildrenForKnownAttributes(decl, this);
Expand All @@ -860,7 +894,7 @@ void AbstractFunctionDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint(

if (!isa<AccessorDecl>(decl)) {
leaf = scopeCreator.addNestedGenericParamScopesToTree(
decl, decl->getGenericParams(), leaf);
decl, getPotentiallyOpaqueGenericParams(decl), leaf);

auto *params = decl->getParameters();
if (params->size() > 0) {
Expand Down Expand Up @@ -1008,7 +1042,7 @@ void SubscriptDeclScope::expandAScopeThatDoesNotCreateANewInsertionPoint(
ScopeCreator &scopeCreator) {
scopeCreator.addChildrenForKnownAttributes(decl, this);
auto *leaf = scopeCreator.addNestedGenericParamScopesToTree(
decl, decl->getGenericParams(), this);
decl, getPotentiallyOpaqueGenericParams(decl), this);
scopeCreator.constructExpandAndInsert<ParameterListScope>(
leaf, decl->getIndices(), decl->getAccessor(AccessorKind::Get));
scopeCreator.addChildrenForParsedAccessors(decl, leaf);
Expand Down
8 changes: 8 additions & 0 deletions lib/AST/ASTScopeLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,14 @@ bool GenericTypeOrExtensionScope::areMembersVisibleFromWhereClause() const {
NullablePtr<const ASTScopeImpl>
PatternEntryInitializerScope::getLookupParent() const {
auto parent = getParent().get();

// Skip generic parameter scopes, which occur here due to named opaque
// result types.
// FIXME: Proper isa/dyn_cast support would be better than a string
// comparison here.
while (parent->getClassName() == "GenericParamScope")
parent = parent->getLookupParent().get();

ASTScopeAssert(parent->getClassName() == "PatternEntryDeclScope",
"PatternEntryInitializerScope in unexpected place");

Expand Down
9 changes: 9 additions & 0 deletions lib/AST/ASTScopeSourceRange.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,15 @@ SourceRange GenericParamScope::getSourceRangeOfThisASTNode(
return SourceRange(getLocAfterExtendedNominal(ext), ext->getEndLoc());
}

// For a variable, the generic parameter is visible throughout the pattern
// binding entry.
if (auto var = dyn_cast<VarDecl>(holder)) {
if (auto patternBinding = var->getParentPatternBinding()) {
unsigned index = patternBinding->getPatternEntryIndexForVarDecl(var);
return patternBinding->getPatternList()[index].getSourceRange();
}
}

// For all other declarations, generic parameters are visible everywhere.
return holder->getSourceRange();
}
Expand Down
13 changes: 13 additions & 0 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7829,6 +7829,19 @@ bool OpaqueTypeDecl::isOpaqueReturnTypeOfFunction(
return false;
}

bool OpaqueTypeDecl::hasExplicitGenericParams() const {
return getExplicitGenericParam(0) != nullptr;
}

GenericTypeParamDecl *OpaqueTypeDecl::getExplicitGenericParam(
unsigned ordinal) const {
if (ordinal >= getOpaqueGenericParams().size())
return nullptr;

auto genericParamType = getOpaqueGenericParams()[ordinal];
return genericParamType->getDecl();
}

unsigned OpaqueTypeDecl::getAnonymousOpaqueParamOrdinal(
OpaqueReturnTypeRepr *repr) const {
assert(NamingDeclAndHasOpaqueReturnTypeRepr.getInt() &&
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,7 @@ Type ConstraintSystem::openOpaqueType(OpaqueTypeArchetypeType *opaque,
if (knownReplacements != OpenedTypes.end()) {
auto param = opaque->getInterfaceType()->castTo<GenericTypeParamType>();
for (const auto &replacement : knownReplacements->second) {
if (replacement.first == param)
if (replacement.first->isEqual(param))
return replacement.second;
}

Expand Down
38 changes: 23 additions & 15 deletions lib/Sema/MiscDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2610,6 +2610,7 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
OpaqueTypeDecl *OpaqueDecl;
BraceStmt *Body;
SmallVector<Candidate, 4> Candidates;
SmallPtrSet<const void *, 4> KnownCandidates;

bool HasInvalidReturn = false;

Expand Down Expand Up @@ -2644,13 +2645,7 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
// Check whether all of the underlying type candidates match up.
// TODO [OPAQUE SUPPORT]: diagnose multiple opaque types
SubstitutionMap underlyingSubs = Candidates.front().second;
SubstitutionMap canonicalUnderlyingSubs = underlyingSubs.getCanonical();
bool mismatch =
std::any_of(Candidates.begin() + 1, Candidates.end(),
[&](Candidate &otherCandidate) {
return canonicalUnderlyingSubs != otherCandidate.second.getCanonical();
});
if (mismatch) {
if (Candidates.size() > 1) {
unsigned mismatchIndex = OpaqueDecl->getOpaqueGenericParams().size();
for (auto genericParam : OpaqueDecl->getOpaqueGenericParams()) {
unsigned index = genericParam->getIndex();
Expand All @@ -2669,12 +2664,21 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {
break;
}
assert(mismatchIndex < OpaqueDecl->getOpaqueGenericParams().size());
TypeRepr *opaqueRepr =
OpaqueDecl->getOpaqueReturnTypeReprs()[mismatchIndex];
Implementation->diagnose(
diag::opaque_type_mismatched_underlying_type_candidates,
opaqueRepr)
.highlight(opaqueRepr->getSourceRange());

if (auto genericParam =
OpaqueDecl->getExplicitGenericParam(mismatchIndex)) {
Implementation->diagnose(
diag::opaque_type_mismatched_underlying_type_candidates_named,
genericParam->getName())
.highlight(genericParam->getLoc());
} else {
TypeRepr *opaqueRepr =
OpaqueDecl->getOpaqueReturnTypeReprs()[mismatchIndex];
Implementation->diagnose(
diag::opaque_type_mismatched_underlying_type_candidates,
opaqueRepr)
.highlight(opaqueRepr->getSourceRange());
}

for (auto candidate : Candidates) {
Ctx.Diags.diagnose(
Expand Down Expand Up @@ -2712,8 +2716,12 @@ class OpaqueUnderlyingTypeChecker : public ASTWalker {

std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
if (auto underlyingToOpaque = dyn_cast<UnderlyingToOpaqueExpr>(E)) {
Candidates.push_back(std::make_pair(underlyingToOpaque->getSubExpr(),
underlyingToOpaque->substitutions));
auto key =
underlyingToOpaque->substitutions.getCanonical().getOpaqueValue();
if (KnownCandidates.insert(key).second) {
Candidates.push_back(std::make_pair(underlyingToOpaque->getSubExpr(),
underlyingToOpaque->substitutions));
}
return {false, E};
}
return {true, E};
Expand Down
13 changes: 11 additions & 2 deletions lib/Sema/TypeCheckConstraints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -541,14 +541,23 @@ bool TypeChecker::typeCheckPatternBinding(PatternBindingDecl *PBD,
// Bind a property with an opaque return type to the underlying type
// given by the initializer.
if (auto var = pattern->getSingleVar()) {
SubstitutionMap substitutions;
if (auto opaque = var->getOpaqueResultTypeDecl()) {
init->forEachChildExpr([&](Expr *expr) -> Expr * {
if (auto coercionExpr = dyn_cast<UnderlyingToOpaqueExpr>(expr)) {
opaque->setUnderlyingTypeSubstitutions(
coercionExpr->substitutions.mapReplacementTypesOutOfContext());
auto newSubstitutions =
coercionExpr->substitutions.mapReplacementTypesOutOfContext();
if (substitutions.empty()) {
substitutions = newSubstitutions;
} else {
assert(substitutions.getCanonical() ==
newSubstitutions.getCanonical());
}
}
return expr;
});

opaque->setUnderlyingTypeSubstitutions(substitutions);
}
}

Expand Down
Loading