Skip to content

Preliminary Sema and AST support for subclass existentials (SE-0156) #8650

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 20 commits into from
Apr 11, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b9494b0
AST: Add an assertion to MemberRefExpr constructor
slavapestov Apr 8, 2017
efb3d42
AST: Do the right thing when constructing ConcreteDeclRef with empty …
slavapestov Apr 8, 2017
143c91a
Sema: Remove unused method declaration
slavapestov Apr 10, 2017
011fb81
Sema: Fix a compiler crasher
slavapestov Apr 10, 2017
6b4c877
Sema: simplifyType() can return early if the type has no variables
slavapestov Apr 8, 2017
ed4c681
Sema: Simplify Solution::computeSubstitutions()
slavapestov Apr 8, 2017
819dfd5
Sema: Better type safety for opened types map
slavapestov Apr 8, 2017
54883e8
Sema: Fix type lookup from protocol extensions with a class-constrain…
slavapestov Apr 9, 2017
95e2802
Sema: Test inheritance clause references to nested type of class-cons…
slavapestov Apr 9, 2017
9c3b514
AST: New getExistentialLayout() method on TypeBase and CanType
slavapestov Apr 8, 2017
d7c1d81
AST: Update GenericSignatureBuilder for subclass existentials
slavapestov Apr 10, 2017
32a7505
AST: Update ProtocolCompositionType::get() for subclass existentials
slavapestov Apr 9, 2017
ad417c9
AST: Update ProtocolCompositionType::requiresClass() for subclass exi…
slavapestov Apr 9, 2017
30cd0a9
AST: Update TypeBase::getSuperclass() for subclass existentials
slavapestov Apr 9, 2017
473faf1
AST: Update TypeBase::getSuperclassForDecl() for subclass existentials
slavapestov Apr 8, 2017
b1d22ff
AST: Update TypeBase::mayHaveSuperclass() for subclass existentials
slavapestov Apr 10, 2017
4efa9ea
Sema: Update getTypeOfMemberReference() for subclass existentials
slavapestov Apr 8, 2017
2e5b3b6
Sema: Update constraint solver for subclass existentials
slavapestov Apr 10, 2017
1b2252f
Sema: Update TypeChecker::checkInheritanceClause() for subclass exist…
slavapestov Apr 9, 2017
de323b5
Sema: Update resolveType() for subclass existentials
slavapestov Apr 6, 2017
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
8 changes: 6 additions & 2 deletions include/swift/AST/ConcreteDeclRef.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,12 @@ class ConcreteDeclRef {
/// given declaration. This array will be copied into the ASTContext by the
/// constructor.
ConcreteDeclRef(ASTContext &ctx, ValueDecl *decl,
SubstitutionList substitutions)
: Data(SpecializedDeclRef::create(ctx, decl, substitutions)) { }
SubstitutionList substitutions) {
if (substitutions.empty())
Data = decl;
else
Data = SpecializedDeclRef::create(ctx, decl, substitutions);
}

/// Determine whether this declaration reference refers to anything.
explicit operator bool() const { return !Data.isNull(); }
Expand Down
6 changes: 3 additions & 3 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -699,9 +699,9 @@ ERROR(tuple_type_multiple_labels,none,

// Protocol Types
ERROR(expected_rangle_protocol,PointsToFirstBadToken,
"expected '>' to complete protocol composition type", ())
"expected '>' to complete protocol-constrained type", ())
ERROR(disallowed_protocol_composition,PointsToFirstBadToken,
"protocol composition is neither allowed nor needed here", ())
"protocol-constrained type is neither allowed nor needed here", ())

WARNING(deprecated_protocol_composition,none,
"'protocol<...>' composition syntax is deprecated; join the protocols using '&'", ())
Expand Down Expand Up @@ -1361,7 +1361,7 @@ ERROR(unexpected_class_constraint,none,
NOTE(suggest_anyobject,none,
"did you mean to constrain %0 with the 'AnyObject' protocol?", (Identifier))
ERROR(expected_generics_type_restriction,none,
"expected a type name or protocol composition restricting %0",
"expected a class type or protocol-constrained type restricting %0",
(Identifier))
ERROR(requires_single_equal,none,
"use '==' for same-type requirements rather than '='", ())
Expand Down
13 changes: 9 additions & 4 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1548,14 +1548,19 @@ ERROR(circular_protocol_def,none,
"circular protocol inheritance %0", (StringRef))
NOTE(protocol_here,none,
"protocol %0 declared here", (Identifier))
ERROR(protocol_composition_not_protocol,none,
"non-protocol type %0 cannot be used within a protocol composition", (Type))
ERROR(objc_protocol_inherits_non_objc_protocol,none,
"@objc protocol %0 cannot refine non-@objc protocol %1", (Type, Type))
WARNING(protocol_composition_with_postfix,none,
"protocol composition with postfix '%0' is ambiguous "
"protocol-constrained type with postfix '%0' is ambiguous "
"and will be rejected in future version of Swift", (StringRef))

ERROR(invalid_protocol_composition_member,none,
"non-protocol, non-class type %0 cannot be used within a "
"protocol-constrained type", (Type))
ERROR(protocol_composition_one_class,none,
"protocol-constrained type cannot contain class %0 because it already "
"contains class %1", (Type, Type))

ERROR(requires_conformance_nonprotocol,none,
"type %0 constrained to non-protocol type %1", (TypeLoc, TypeLoc))
ERROR(requires_not_suitable_archetype,none,
Expand Down Expand Up @@ -3086,7 +3091,7 @@ NOTE(not_objc_empty_protocol_composition,none,
NOTE(not_objc_protocol,none,
"protocol %0 is not '@objc'", (Type))
NOTE(not_objc_error_protocol_composition,none,
"protocol composition involving 'Error' cannot be represented "
"protocol-constrained type containing 'Error' cannot be represented "
"in Objective-C", ())
NOTE(not_objc_empty_tuple,none,
"empty tuple type cannot be represented in Objective-C", ())
Expand Down
80 changes: 80 additions & 0 deletions include/swift/AST/ExistentialLayout.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//===--- ExistentialLayout.h - Existential type decomposition ---*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 ExistentialLayout struct.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_EXISTENTIAL_LAYOUT_H
#define SWIFT_EXISTENTIAL_LAYOUT_H

#include "swift/AST/ASTContext.h"
#include "swift/AST/Type.h"
#include "llvm/ADT/SmallVector.h"

namespace swift {
class ProtocolDecl;
class ProtocolType;
class ProtocolCompositionType;

struct ExistentialLayout {
ExistentialLayout() {
requiresClass = false;
requiresClassImplied = false;
containsNonObjCProtocol = false;
singleProtocol = nullptr;
}

ExistentialLayout(ProtocolType *type);
ExistentialLayout(ProtocolCompositionType *type);

/// The superclass constraint, if any.
Type superclass;

/// Whether the existential requires a class, either via an explicit
/// '& AnyObject' member or because of a superclass or protocol constraint.
bool requiresClass : 1;
Copy link
Member

Choose a reason for hiding this comment

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

Should this more generally have a LayoutConstraintKind ?


/// Whether the class constraint was implied by another constraint and therefore
/// does not need to be stated explicitly.
bool requiresClassImplied : 1;

/// Whether any protocol members are non-@objc.
bool containsNonObjCProtocol : 1;

bool isAnyObject() const;

bool isObjC() const {
// FIXME: Does the superclass have to be @objc?
return requiresClass && !containsNonObjCProtocol;
}

bool isExistentialWithError(ASTContext &ctx) const;

ArrayRef<ProtocolType *> getProtocols() const {
if (singleProtocol)
return ArrayRef<ProtocolType *>{&singleProtocol, 1};
return multipleProtocols;
}

private:
// Inline storage for 'protocols' member above when computing
// layout of a single ProtocolType
ProtocolType *singleProtocol;

/// Zero or more protocol constraints.
ArrayRef<ProtocolType *> multipleProtocols;
};

}

#endif // SWIFT_EXISTENTIAL_LAYOUT_H
12 changes: 6 additions & 6 deletions include/swift/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class SubstitutionMap;
class TypeBase;
class Type;
class TypeWalker;
struct ExistentialLayout;

/// \brief Type substitution mapping from substitutable types to their
/// replacements.
Expand Down Expand Up @@ -410,16 +411,15 @@ class CanType : public Type {
/// Given that this type is an existential, return its
/// protocols in a canonical order.
void getExistentialTypeProtocols(
SmallVectorImpl<ProtocolDecl *> &protocols) {
return getExistentialTypeProtocolsImpl(*this, protocols);
}
SmallVectorImpl<ProtocolDecl *> &protocols);

/// Given that this type is any kind of existential, return its
/// protocols in a canonical order.
void getAnyExistentialTypeProtocols(
SmallVectorImpl<ProtocolDecl *> &protocols) {
return getAnyExistentialTypeProtocolsImpl(*this, protocols);
}
SmallVectorImpl<ProtocolDecl *> &protocols);

/// Break an existential down into a set of constraints.
ExistentialLayout getExistentialLayout();

/// Is this an ObjC-compatible existential type?
bool isObjCExistentialType() const {
Expand Down
24 changes: 15 additions & 9 deletions include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,9 @@ class alignas(1 << TypeAlignInBits) TypeBase {
/// its list of protocols.
void getAnyExistentialTypeProtocols(SmallVectorImpl<ProtocolDecl *> &protocols);

/// Break an existential down into a set of constraints.
ExistentialLayout getExistentialLayout();

/// Determines the element type of a known *UnsafeMutablePointer
/// variant, or returns null if the type is not a pointer.
Type getAnyPointerElementType(PointerTypeKind &PTK);
Expand Down Expand Up @@ -3602,7 +3605,7 @@ class ProtocolType : public NominalType, public llvm::FoldingSetNode {
}

/// True if only classes may conform to the protocol.
bool requiresClass() const;
bool requiresClass();

// Implement isa/cast/dyncast/etc.
static bool classof(const TypeBase *T) {
Expand Down Expand Up @@ -3669,16 +3672,13 @@ class ProtocolCompositionType : public TypeBase, public llvm::FoldingSetNode {
/// \brief Retrieve the set of protocols composed to create this type.
ArrayRef<Type> getProtocols() const { return Protocols; }

/// \brief Return the protocols of this type in canonical order.
void getAnyExistentialTypeProtocols(SmallVectorImpl<ProtocolDecl *> &protos);

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, Protocols);
}
static void Profile(llvm::FoldingSetNodeID &ID, ArrayRef<Type> Protocols);

/// True if one or more of the protocols is class.
bool requiresClass() const;
bool requiresClass();

// Implement isa/cast/dyncast/etc.
static bool classof(const TypeBase *T) {
Expand All @@ -3689,10 +3689,11 @@ class ProtocolCompositionType : public TypeBase, public llvm::FoldingSetNode {
static ProtocolCompositionType *build(const ASTContext &C,
ArrayRef<Type> Protocols);

ProtocolCompositionType(const ASTContext *Ctx, ArrayRef<Type> Protocols)
: TypeBase(TypeKind::ProtocolComposition, /*Context=*/Ctx,
RecursiveTypeProperties()),
Protocols(Protocols) { }
ProtocolCompositionType(const ASTContext *ctx, ArrayRef<Type> protocols,
RecursiveTypeProperties properties)
: TypeBase(TypeKind::ProtocolComposition, /*Context=*/ctx,
properties),
Protocols(protocols) { }
};
BEGIN_CAN_TYPE_WRAPPER(ProtocolCompositionType, Type)
/// In the canonical representation, these are all ProtocolTypes.
Expand Down Expand Up @@ -4555,9 +4556,14 @@ inline bool TypeBase::mayHaveSuperclass() {
if (getClassOrBoundGenericClass())
return true;

// FIXME: requiresClass() is not the same as having an explicit superclass;
// is this wrong?
if (auto archetype = getAs<ArchetypeType>())
return (bool)archetype->requiresClass();

if (isExistentialType())
return (bool)getSuperclass(nullptr);

return is<DynamicSelfType>();
}

Expand Down
3 changes: 3 additions & 0 deletions include/swift/Basic/LangOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ namespace swift {
/// optimized custom allocator, so that memory debugging tools can be used.
bool UseMalloc = false;

/// \brief Enable classes to appear in protocol composition types.
bool EnableExperimentalSubclassExistentials = false;

/// \brief Enable experimental property behavior feature.
bool EnableExperimentalPropertyBehaviors = false;

Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ def enable_experimental_deserialization_recovery :
Flag<["-"], "enable-experimental-deserialization-recovery">,
HelpText<"Attempt to recover from missing xrefs (etc) in swiftmodules">;

def enable_experimental_subclass_existentials : Flag<["-"],
"enable-experimental-subclass-existentials">,
HelpText<"Enable classes to appear in protocol composition types">;

def enable_cow_existentials : Flag<["-"], "enable-cow-existentials">,
HelpText<"Enable the copy-on-write existential implementation">;

Expand Down
42 changes: 27 additions & 15 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "swift/AST/ASTContext.h"
#include "ForeignRepresentationInfo.h"
#include "swift/Strings.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericSignatureBuilder.h"
#include "swift/AST/ConcreteDeclRef.h"
#include "swift/AST/DiagnosticEngine.h"
Expand Down Expand Up @@ -269,6 +270,7 @@ struct ASTContext::Implementation {
llvm::FoldingSet<UnboundGenericType> UnboundGenericTypes;
llvm::FoldingSet<BoundGenericType> BoundGenericTypes;
llvm::FoldingSet<ProtocolType> ProtocolTypes;
llvm::FoldingSet<ProtocolCompositionType> ProtocolCompositionTypes;
llvm::FoldingSet<LayoutConstraintInfo> LayoutConstraints;

/// The set of normal protocol conformances.
Expand Down Expand Up @@ -311,7 +313,6 @@ struct ASTContext::Implementation {
llvm::DenseMap<CanType, SILBlockStorageType *> SILBlockStorageTypes;
llvm::FoldingSet<SILBoxType> SILBoxTypes;
llvm::DenseMap<BuiltinIntegerWidth, BuiltinIntegerType*> IntegerTypes;
llvm::FoldingSet<ProtocolCompositionType> ProtocolCompositionTypes;
llvm::FoldingSet<BuiltinVectorType> BuiltinVectorTypes;
llvm::FoldingSet<GenericSignature> GenericSignatures;
llvm::FoldingSet<DeclName::CompoundDeclName> CompoundNames;
Expand Down Expand Up @@ -2797,23 +2798,31 @@ ProtocolCompositionType::build(const ASTContext &C, ArrayRef<Type> Protocols) {
void *InsertPos = nullptr;
llvm::FoldingSetNodeID ID;
ProtocolCompositionType::Profile(ID, Protocols);
if (ProtocolCompositionType *Result
= C.Impl.ProtocolCompositionTypes.FindNodeOrInsertPos(ID, InsertPos))
return Result;

bool isCanonical = true;
RecursiveTypeProperties properties;
for (Type t : Protocols) {
if (!t->isCanonical())
isCanonical = false;
properties |= t->getRecursiveProperties();
}

// Create a new protocol composition type.
ProtocolCompositionType *New
= new (C, AllocationArena::Permanent)
auto arena = getArena(properties);

if (auto compTy
= C.Impl.getArena(arena).ProtocolCompositionTypes
.FindNodeOrInsertPos(ID, InsertPos))
return compTy;

auto compTy
= new (C, arena)
ProtocolCompositionType(isCanonical ? &C : nullptr,
C.AllocateCopy(Protocols));
C.Impl.ProtocolCompositionTypes.InsertNode(New, InsertPos);
return New;
C.AllocateCopy(Protocols),
properties);
C.Impl.getArena(arena).ProtocolCompositionTypes
.InsertNode(compTy, InsertPos);
return compTy;
}

ReferenceStorageType *ReferenceStorageType::get(Type T, Ownership ownership,
Expand Down Expand Up @@ -3454,19 +3463,22 @@ CanArchetypeType ArchetypeType::getOpened(Type existential,
knownID = UUID::fromTime();
}

llvm::SmallVector<ProtocolDecl *, 4> conformsTo;
assert(existential->isExistentialType());
existential->getExistentialTypeProtocols(conformsTo);
Type superclass = existential->getSuperclass(nullptr);
auto layout = existential->getExistentialLayout();

SmallVector<ProtocolDecl *, 2> protos;
for (auto proto : layout.getProtocols())
protos.push_back(proto->getDecl());

auto arena = AllocationArena::Permanent;
void *mem = ctx.Allocate(
totalSizeToAlloc<ProtocolDecl *, Type, LayoutConstraint, UUID>(
conformsTo.size(), superclass ? 1 : 0, 0, 1),
protos.size(), layout.superclass ? 1 : 0, 0, 1),
alignof(ArchetypeType), arena);

// FIXME: Pass in class layout constraint
auto result =
::new (mem) ArchetypeType(ctx, existential, conformsTo, superclass,
::new (mem) ArchetypeType(ctx, existential,
protos, layout.superclass,
existential->getLayoutConstraint(), *knownID);
openedExistentialArchetypes[*knownID] = result;

Expand Down
16 changes: 8 additions & 8 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2727,16 +2727,16 @@ ProtocolDecl::getInheritedProtocols() const {
// FIXME: Gather inherited protocols from the "inherited" list.
// We shouldn't need this, but it shows up in recursive invocations.
if (!isRequirementSignatureComputed()) {
SmallPtrSet<ProtocolDecl *, 4> known;
for (auto inherited : getInherited()) {
SmallPtrSet<ProtocolDecl *, 4> known;
if (auto type = inherited.getType()) {
if (type->isExistentialType()) {
SmallVector<ProtocolDecl *, 4> protocols;
type->getExistentialTypeProtocols(protocols);
for (auto proto : protocols) {
if (known.insert(proto).second)
result.push_back(proto);
}
// Only protocols can appear in the inheritance clause
// of a protocol -- anything else should get diagnosed
// elsewhere.
if (auto *protoTy = type->getAs<ProtocolType>()) {
Copy link
Member

Choose a reason for hiding this comment

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

Wait, really? I thought we could have typealiases of protocol compositions, as well as superclass types, in this list?

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'll add more tests and see what we support exactly.

auto *protoDecl = protoTy->getDecl();
if (known.insert(protoDecl).second)
result.push_back(protoDecl);
}
}
}
Expand Down
1 change: 1 addition & 0 deletions lib/AST/Expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1321,6 +1321,7 @@ MemberRefExpr::MemberRefExpr(Expr *base, SourceLoc dotLoc,

MemberRefExprBits.Semantics = (unsigned) semantics;
MemberRefExprBits.IsSuper = false;
assert(Member);
}

Type OverloadSetRefExpr::getBaseType() const {
Expand Down
Loading