Skip to content

Commit

Permalink
[SE-0095] [Runtime], [Demangler], & AST printer updated to new compos…
Browse files Browse the repository at this point in the history
…ition syntax

- All parts of the compiler now use ‘P1 & P2’ syntax
- The demangler and AST printer wrap the composition in parens if it is
in a metatype lookup
- IRGen mangles compositions differently
    - “protocol<>” is now “swift.Any”
    - “protocol<_TP1P,_TP1Q>” is now “_TP1P&_TP1Q”
- Tests cases are updated and added to test the new syntax and mangling
  • Loading branch information
joewillsher authored and DougGregor committed Jul 19, 2016
1 parent 622c86b commit 3938d56
Show file tree
Hide file tree
Showing 68 changed files with 321 additions and 309 deletions.
2 changes: 1 addition & 1 deletion include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -3476,7 +3476,7 @@ END_CAN_TYPE_WRAPPER(ProtocolType, NominalType)
/// \code
/// protocol P { /* ... */ }
/// protocol Q { /* ... */ }
/// var x : protocol<P, Q>
/// var x : P & Q
/// \endcode
///
/// Here, the type of x is a composition of the protocols 'P' and 'Q'.
Expand Down
26 changes: 17 additions & 9 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3294,6 +3294,12 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
return !arch->isOpenedExistential();
}

case TypeKind::ProtocolComposition: {
// 'Any' and single protocol compositions are simple
auto composition = type->getAs<ProtocolCompositionType>();
return composition->getProtocols().size() <= 1;
}

default:
return true;
}
Expand Down Expand Up @@ -4043,16 +4049,18 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
}

void visitProtocolCompositionType(ProtocolCompositionType *T) {
Printer << tok::kw_protocol << "<";
bool First = true;
for (auto Proto : T->getProtocols()) {
if (First)
First = false;
else
Printer << ", ";
visit(Proto);
if (T->getProtocols().empty()) {
Printer << "Any";
} else {
bool First = true;
for (auto Proto : T->getProtocols()) {
if (First)
First = false;
else
Printer << " & ";
visit(Proto);
}
}
Printer << ">";
}

void visitLValueType(LValueType *T) {
Expand Down
8 changes: 1 addition & 7 deletions lib/AST/ArchetypeBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -826,23 +826,17 @@ void ArchetypeBuilder::PotentialArchetype::dump(llvm::raw_ostream &Out,
if (!ConformsTo.empty()) {
Out << " : ";

if (ConformsTo.size() != 1)
Out << "protocol<";

bool First = true;
for (const auto &ProtoAndSource : ConformsTo) {
if (First)
First = false;
else
Out << ", ";
Out << " & ";

Out << ProtoAndSource.first->getName().str() << " [";
ProtoAndSource.second.dump(Out, SrcMgr);
Out << "]";
}

if (ConformsTo.size() != 1)
Out << ">";
}

if (Representative != this) {
Expand Down
20 changes: 11 additions & 9 deletions lib/AST/TypeRepr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -434,16 +434,18 @@ ProtocolCompositionTypeRepr::create(ASTContext &C,

void ProtocolCompositionTypeRepr::printImpl(ASTPrinter &Printer,
const PrintOptions &Opts) const {
Printer << "protocol<";
bool First = true;
for (auto Proto : Protocols) {
if (First)
First = false;
else
Printer << ", ";
printTypeRepr(Proto, Printer, Opts);
if (Protocols.empty()) {
Printer << "Any";
} else {
bool First = true;
for (auto Proto : Protocols) {
if (First)
First = false;
else
Printer << " & ";
printTypeRepr(Proto, Printer, Opts);
}
}
Printer << ">";
}

void MetatypeTypeRepr::printImpl(ASTPrinter &Printer,
Expand Down
10 changes: 4 additions & 6 deletions lib/Basic/Demangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3465,12 +3465,10 @@ void NodePrinter::print(NodePointer pointer, bool asContext, bool suppressType)
NodePointer type_list = pointer->getChild(0);
if (!type_list)
return;
bool needs_proto_marker = (type_list->getNumChildren() != 1);
if (needs_proto_marker)
Printer << "protocol<";
printChildren(type_list, ", ");
if (needs_proto_marker)
Printer << ">";
if (type_list->getNumChildren() == 0)
Printer << "Any";
else
printChildren(type_list, " & ");
return;
}
case Node::Kind::Archetype: {
Expand Down
2 changes: 1 addition & 1 deletion lib/IDE/TypeReconstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1890,7 +1890,7 @@ static void VisitNodeProtocolList(
nodes.push_back(cur_node->getFirstChild());
VisitNode(ast, nodes, protocol_types_result, generic_context);
if (protocol_types_result._error
.empty() /* cannot check for empty type list as protocol<> is allowed */) {
.empty() /* cannot check for empty type list as Any is allowed */) {
if (ast) {
result._types.push_back(
ProtocolCompositionType::get(*ast, protocol_types_result._types));
Expand Down
3 changes: 2 additions & 1 deletion lib/IRGen/GenExistential.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ class ExistentialTypeInfoBase : public Base,

/// A TypeInfo implementation for existential types, i.e., types like:
/// Printable
/// protocol<Printable, Serializable>
/// Printable & Serializable
/// Any
/// with the semantic translation:
/// \exists t : Printable . t
/// t here is an ArchetypeType.
Expand Down
16 changes: 9 additions & 7 deletions lib/IRGen/GenType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1657,16 +1657,18 @@ llvm::StructType *IRGenModule::createNominalType(CanType type) {
llvm::StructType *
IRGenModule::createNominalType(ProtocolCompositionType *type) {
llvm::SmallString<32> typeName;

SmallVector<ProtocolDecl *, 4> protocols;
type->getAnyExistentialTypeProtocols(protocols);

typeName.append("protocol<");
for (unsigned i = 0, e = protocols.size(); i != e; ++i) {
if (i) typeName.push_back(',');
LinkEntity::forNonFunction(protocols[i]).mangle(typeName);

if (protocols.empty()) {
typeName.append("swift.Any");
} else {
for (unsigned i = 0, e = protocols.size(); i != e; ++i) {
if (i) typeName.push_back('&');
LinkEntity::forNonFunction(protocols[i]).mangle(typeName);
}
}
typeName.push_back('>');
return llvm::StructType::create(getLLVMContext(), typeName.str());
}

Expand Down
39 changes: 19 additions & 20 deletions stdlib/public/runtime/Casting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,34 +123,33 @@ static const char *_getProtocolName(const ProtocolDescriptor *protocol) {
}

static void _buildExistentialTypeName(const ProtocolDescriptorList *protocols,
TypeSyntaxLevel level,
bool qualified,
std::string &result) {
auto options = Demangle::DemangleOptions();
options.QualifyEntities = qualified;
options.DisplayDebuggerGeneratedModule = false;

// If there's only one protocol, the existential type name is the protocol
// name.
// name. If there are 0 protocols it is 'Any'
auto descriptors = protocols->getProtocols();
auto numProtocols = protocols->NumProtocols;

if (protocols->NumProtocols == 1) {
auto name = _getProtocolName(descriptors[0]);
result += Demangle::demangleTypeAsString(name,
strlen(name),
options);
return;
}

result += "protocol<";
for (unsigned i = 0, e = protocols->NumProtocols; i < e; ++i) {
if (i > 0)
result += ", ";
auto name = _getProtocolName(descriptors[i]);
result += Demangle::demangleTypeAsString(name,
strlen(name),
options);
if (numProtocols == 0) {
result += "Any";
} else {
// compositions of more than 1 protocol need parens in .Type contexts
bool needsParens = level >= TypeSyntaxLevel::TypeSimple && numProtocols != 1;
if (needsParens) result += "(";
for (unsigned i = 0, e = numProtocols; i < e; ++i) {
if (i) result += " & ";
auto name = _getProtocolName(descriptors[i]);
result += Demangle::demangleTypeAsString(name,
strlen(name),
options);
}
if (needsParens) result += ")";
}
result += ">";
}

static void _buildFunctionTypeName(const FunctionTypeMetadata *func,
Expand Down Expand Up @@ -233,7 +232,7 @@ static void _buildNameForMetadata(const Metadata *type,
}
case MetadataKind::Existential: {
auto exis = static_cast<const ExistentialTypeMetadata *>(type);
_buildExistentialTypeName(&exis->Protocols, qualified, result);
_buildExistentialTypeName(&exis->Protocols, level, qualified, result);
return;
}
case MetadataKind::ExistentialMetatype: {
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/runtime/Reflection.mm
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ - (id)debugQuickLookObject;

namespace {

/// The layout of protocol<>.
/// The layout of Any.
using Any = OpaqueExistentialContainer;

// Swift assumes Any is returned in memory.
Expand Down
6 changes: 3 additions & 3 deletions test/1_stdlib/PrintDiagnostics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

var stream = ""

print(3, &stream) // expected-error{{'&' used with non-inout argument of type 'protocol<>'}}
debugPrint(3, &stream) // expected-error{{'&' used with non-inout argument of type 'protocol<>'}}
print(3, &stream) // expected-error{{'&' used with non-inout argument of type 'Any'}}
debugPrint(3, &stream) // expected-error{{'&' used with non-inout argument of type 'Any'}}
print(3, &stream, appendNewline: false) // expected-error {{cannot pass immutable value as inout argument: implicit conversion from 'String' to 'OutputStream' requires a temporary}}
debugPrint(3, &stream, appendNewline: false) // expected-error {{cannot pass immutable value as inout argument: implicit conversion from 'String' to 'OutputStream' requires a temporary}}
print(4, quack: 5) // expected-error {{argument labels '(_:, quack:)' do not match any available overloads}}
// expected-note@-1{{overloads for 'print' exist with these partially matching parameter lists: (protocol<>..., separator: String, terminator: String), (T, appendNewline: Bool), (T, inout OutputStream), (T, inout OutputStream, appendNewline: Bool)}}
// expected-note@-1{{overloads for 'print' exist with these partially matching parameter lists: (Any..., separator: String, terminator: String), (T, appendNewline: Bool), (T, inout OutputStream), (T, inout OutputStream, appendNewline: Bool)}}
6 changes: 3 additions & 3 deletions test/1_stdlib/Runtime.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ Runtime.test("typeName") {
expectEqual("a.SomeClass", _typeName(SomeClass.self))
expectEqual("a.SomeStruct", _typeName(SomeStruct.self))
expectEqual("a.SomeEnum", _typeName(SomeEnum.self))
expectEqual("protocol<>.Protocol", _typeName(Any.Protocol.self))
expectEqual("Any.Protocol", _typeName(Any.Protocol.self))
expectEqual("Swift.AnyObject.Protocol", _typeName(AnyObject.Protocol.self))
expectEqual("Swift.AnyObject.Type.Protocol", _typeName(AnyClass.Protocol.self))
expectEqual("Swift.Optional<Swift.AnyObject>.Type", _typeName((AnyObject?).Type.self))
Expand All @@ -336,7 +336,7 @@ Runtime.test("typeName") {
_typeName(a.dynamicType))

a = Any.self
expectEqual("protocol<>.Protocol", _typeName(a.dynamicType))
expectEqual("Any.Protocol", _typeName(a.dynamicType))
}

class SomeSubclass : SomeClass {}
Expand Down Expand Up @@ -562,7 +562,7 @@ Reflection.test("Struct/Generic/DefaultMirror") {
dump(value, to: &output)

let expected =
"▿ a.GenericStructWithDefaultMirror<Swift.Int, Swift.Array<Swift.Optional<protocol<>>>>\n" +
"▿ a.GenericStructWithDefaultMirror<Swift.Int, Swift.Array<Swift.Optional<Any>>>\n" +
" - first: 123\n" +
" ▿ second: 3 elements\n" +
" ▿ Optional(\"abc\")\n" +
Expand Down
5 changes: 2 additions & 3 deletions test/1_stdlib/RuntimeObjC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -767,9 +767,8 @@ Reflection.test("MetatypeMirror") {
dump(objcProtocolConcreteMetatype, to: &output)
expectEqual(expectedObjCProtocolConcrete, output)

typealias Composition = SomeNativeProto & SomeObjCProto
let compositionConcreteMetatype = Composition.self
let expectedComposition = "- protocol<a.SomeNativeProto, a.SomeObjCProto> #0\n"
let compositionConcreteMetatype = (SomeNativeProto & SomeObjCProto).self
let expectedComposition = "- a.SomeNativeProto & a.SomeObjCProto> #0\n"
output = ""
dump(compositionConcreteMetatype, to: &output)
expectEqual(expectedComposition, output)
Expand Down
14 changes: 7 additions & 7 deletions test/1_stdlib/TypeName.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ TypeNameTests.test("Prints") {

expectEqual("main.P", _typeName(P.self))
typealias PP2 = P & P2
expectEqual("protocol<main.P, main.P2>",
expectEqual("main.P & main.P2",
_typeName(PP2.self))
expectEqual("protocol<>", _typeName(Any.self))
expectEqual("protocol<main.P, main.P2>", _typeName((P & P2).self))
expectEqual("Any", _typeName(Any.self))
expectEqual("main.P & main.P2", _typeName((P & P2).self))

typealias F = () -> ()
typealias F2 = () -> () -> ()
Expand All @@ -78,8 +78,8 @@ TypeNameTests.test("Prints") {
expectEqual("((()) -> ()).Type", _typeName(F.Type.self))
expectEqual("main.C.Type", _typeName(C.Type.self))
expectEqual("main.C.Type.Type", _typeName(C.Type.Type.self))
expectEqual("protocol<>.Type", _typeName(Any.Type.self))
expectEqual("protocol<>.Protocol", _typeName(Any.Protocol.self))
expectEqual("Any.Type", _typeName(Any.Type.self))
expectEqual("Any.Protocol", _typeName(Any.Protocol.self))
expectEqual("Swift.AnyObject", _typeName(AnyObject.self))
expectEqual("Swift.AnyObject.Type", _typeName(AnyClass.self))
expectEqual("Swift.Optional<Swift.AnyObject>",
Expand All @@ -88,7 +88,7 @@ TypeNameTests.test("Prints") {


typealias Tup = (Any, F, C)
expectEqual("(protocol<>, (()) -> (), main.C)",
expectEqual("(Any, (()) -> (), main.C)",
_typeName(Tup.self))
}

Expand All @@ -115,7 +115,7 @@ TypeNameTests.test("Inout") {
_typeName(IF3c.self))
expectEqual("(inout ((()) -> ())) -> ()",
_typeName(IF4.self))
expectEqual("(inout Swift.Int, protocol<>) -> ()",
expectEqual("(inout Swift.Int, Any) -> ()",
_typeName(IF5.self))
}

Expand Down
8 changes: 4 additions & 4 deletions test/ClangModules/objc_parse.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ func testProtocols(_ b: B, bp: BProto) {
var c1 : Cat1Proto = b
var bcat1 = b.getAsProtoWithCat()!
c1 = bcat1
bcat1 = c1 // expected-error{{value of type 'Cat1Proto' does not conform to 'protocol<BProto, Cat1Proto>' in assignment}}
bcat1 = c1 // expected-error{{value of type 'Cat1Proto' does not conform to 'BProto & Cat1Proto' in assignment}}
}

// Methods only defined in a protocol
Expand Down Expand Up @@ -505,17 +505,17 @@ func testCStyle() {

func testProtocolQualified(_ obj: CopyableNSObject, cell: CopyableSomeCell,
plainObj: NSObject, plainCell: SomeCell) {
_ = obj as NSObject // expected-error {{'CopyableNSObject' (aka 'protocol<NSCopying, NSObjectProtocol>') is not convertible to 'NSObject'; did you mean to use 'as!' to force downcast?}} {{11-13=as!}}
_ = obj as NSObject // expected-error {{'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol') is not convertible to 'NSObject'; did you mean to use 'as!' to force downcast?}} {{11-13=as!}}
_ = obj as NSObjectProtocol
_ = obj as NSCopying
_ = obj as SomeCell // expected-error {{'CopyableNSObject' (aka 'protocol<NSCopying, NSObjectProtocol>') is not convertible to 'SomeCell'; did you mean to use 'as!' to force downcast?}} {{11-13=as!}}
_ = obj as SomeCell // expected-error {{'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol') is not convertible to 'SomeCell'; did you mean to use 'as!' to force downcast?}} {{11-13=as!}}

_ = cell as NSObject
_ = cell as NSObjectProtocol
_ = cell as NSCopying // expected-error {{'CopyableSomeCell' (aka 'SomeCell') is not convertible to 'NSCopying'; did you mean to use 'as!' to force downcast?}} {{12-14=as!}}
_ = cell as SomeCell

_ = plainObj as CopyableNSObject // expected-error {{'NSObject' is not convertible to 'CopyableNSObject' (aka 'protocol<NSCopying, NSObjectProtocol>'); did you mean to use 'as!' to force downcast?}} {{16-18=as!}}
_ = plainObj as CopyableNSObject // expected-error {{'NSObject' is not convertible to 'CopyableNSObject' (aka 'NSCopying & NSObjectProtocol'); did you mean to use 'as!' to force downcast?}} {{16-18=as!}}
_ = plainCell as CopyableSomeCell // FIXME: This is not really typesafe.
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ func takesProtocol(_ x: Protocol) {}
takesProtocol(ObjCProto.self)
takesProtocol(ObjCProto2.self)
takesProtocol(NonObjCProto.self) // expected-error{{cannot convert value of type 'NonObjCProto.Protocol' to expected argument type 'Protocol'}}
takesProtocol(TwoObjCProtos.self) // expected-error{{cannot convert value of type 'TwoObjCProtos.Protocol' (aka 'protocol<ObjCProto, ObjCProto2>.Protocol') to expected argument type 'Protocol'}}
takesProtocol(TwoObjCProtos.self) // expected-error{{cannot convert value of type 'TwoObjCProtos.Protocol' (aka '(ObjCProto & ObjCProto2).Protocol') to expected argument type 'Protocol'}}
2 changes: 1 addition & 1 deletion test/Constraints/ErrorProtocol_bridging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ var ns4 = compo as NSError

// NSError conversion must be explicit.
// TODO: fixit to insert 'as NSError'
ns4 = compo // expected-error{{cannot assign value of type 'protocol<HairyErrorProtocol, Runcible>' to type 'NSError'}}
ns4 = compo // expected-error{{cannot assign value of type 'HairyErrorProtocol & Runcible' to type 'NSError'}}

let e1 = ns1 as? FooError
let e1fix = ns1 as FooError // expected-error{{did you mean to use 'as!'}} {{17-19=as!}}
Expand Down
Loading

0 comments on commit 3938d56

Please sign in to comment.