diff --git a/include/swift/AST/ASTContext.h b/include/swift/AST/ASTContext.h index e9ba3ea834492..832daf4e0659d 100644 --- a/include/swift/AST/ASTContext.h +++ b/include/swift/AST/ASTContext.h @@ -366,7 +366,20 @@ class ASTContext final { llvm::BumpPtrAllocator & getAllocator(AllocationArena arena = AllocationArena::Permanent) const; + /// init(implicit:) constructors declaring implicit conversions. + /// indexed by nominal of toType then nominal of fromType. + llvm::DenseMap>> ImplicitConversionMap; + public: + llvm::DenseMap> + *implicitConversionsTo(NominalTypeDecl *toNominal, bool create) { + auto constructorsByFromType = ImplicitConversionMap.find(toNominal); + if (constructorsByFromType != ImplicitConversionMap.end()) + return &constructorsByFromType->getSecond(); + return create ? &ImplicitConversionMap[toNominal] : nullptr; + } + /// Allocate - Allocate memory from the ASTContext bump pointer. void *Allocate(unsigned long bytes, unsigned alignment, AllocationArena arena = AllocationArena::Permanent) const { diff --git a/include/swift/AST/Decl.h b/include/swift/AST/Decl.h index b46e6c83df820..a5312c82dc5f4 100644 --- a/include/swift/AST/Decl.h +++ b/include/swift/AST/Decl.h @@ -3155,11 +3155,19 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext { Bits.NominalTypeDecl.IsComputingSemanticMembers = false; } + bool implicitConversionsComputed = false; + friend class ProtocolType; public: using GenericTypeDecl::getASTContext; + bool setConversionsComputed() { + bool wasComputed = implicitConversionsComputed; + implicitConversionsComputed = true; + return wasComputed; + } + SourceRange getBraces() const { return Braces; } void setBraces(SourceRange braces) { Braces = braces; } diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 5188ce4a62bcd..9ed4af2ddf326 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -262,6 +262,7 @@ IDENTIFIER(identifier) IDENTIFIER(_distributedActorRemoteInitialize) IDENTIFIER(_distributedActorDestroy) IDENTIFIER(__isRemoteActor) +IDENTIFIER(implicit) #undef IDENTIFIER #undef IDENTIFIER_ diff --git a/include/swift/Sema/Constraint.h b/include/swift/Sema/Constraint.h index 7d0037e38aa2b..2878237a2cf59 100644 --- a/include/swift/Sema/Constraint.h +++ b/include/swift/Sema/Constraint.h @@ -269,6 +269,8 @@ enum class ConversionRestrictionKind { /// Implicit conversion from a value of CGFloat type to a value of Double type /// via an implicit Double initializer call passing a CGFloat value. CGFloatToDouble, + /// Implicit conversion where an init(implicit:) initializer is available. + ImplicitConversion, }; /// Specifies whether a given conversion requires the creation of a temporary diff --git a/include/swift/Sema/ConstraintSystem.h b/include/swift/Sema/ConstraintSystem.h index 8104e11bafa68..b00dd36011a9f 100644 --- a/include/swift/Sema/ConstraintSystem.h +++ b/include/swift/Sema/ConstraintSystem.h @@ -4243,6 +4243,9 @@ class ConstraintSystem { SmallVectorImpl &conversionsOrFixes, ConstraintLocatorBuilder locator); + /// Is an implicit conversion available from fromType to toType. + ConstructorDecl *implicitConversionAvailable(Type fromType, Type toType); + /// Subroutine of \c matchTypes(), which matches up two tuple types. /// /// \returns the result of performing the tuple-to-tuple conversion. diff --git a/lib/AST/ASTVerifier.cpp b/lib/AST/ASTVerifier.cpp index bf48a403a990c..36779016577ab 100644 --- a/lib/AST/ASTVerifier.cpp +++ b/lib/AST/ASTVerifier.cpp @@ -1136,6 +1136,10 @@ class Verifier : public ASTWalker { } void verifyChecked(TupleExpr *E) { + if (!E->getType()->is()) { + E->getType()->dump(); + return; + } const TupleType *exprTy = E->getType()->castTo(); for_each(exprTy->getElements().begin(), exprTy->getElements().end(), E->getElements().begin(), diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 2f07e578a8f36..9eb12c955d2b5 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -3839,6 +3839,7 @@ void NominalTypeDecl::addExtension(ExtensionDecl *extension) { LastExtension->NextExtension.setPointer(extension); LastExtension = extension; + implicitConversionsComputed = false; addedExtension(extension); } diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 920d735c4da4f..48a0e01024479 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -818,7 +818,7 @@ bool TypeBase::isStdlibType() { bool TypeBase::isCGFloatType() { auto *NTD = getAnyNominal(); - if (!NTD) + if (!NTD || !NTD->getName().is("CGFloat")) return false; auto *DC = NTD->getDeclContext(); @@ -828,9 +828,8 @@ bool TypeBase::isCGFloatType() { auto *module = DC->getParentModule(); // On macOS `CGFloat` is part of a `CoreGraphics` module, // but on Linux it could be found in `Foundation`. - return (module->getName().is("CoreGraphics") || - module->getName().is("Foundation")) && - NTD->getName().is("CGFloat"); + return module->getName().is("CoreGraphics") || + module->getName().is("Foundation"); } bool TypeBase::isKnownStdlibCollectionType() { diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 1a1947bbdf46b..7ff09fa4152a6 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -5606,6 +5606,9 @@ static unsigned getOptionalEvaluationDepth(Expr *expr, Expr *target) { expr = open->getExistentialValue(); // Otherwise, look through implicit conversions. + } else if (auto call = dyn_cast(expr)) { +// expr->dump(); + return depth; } else { expr = cast(expr)->getSubExpr(); } @@ -6633,6 +6636,10 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, if (toType->hasUnresolvedType()) break; + if (cs.implicitConversionAvailable(fromType->lookThroughAllOptionalTypes(), + toType->lookThroughAllOptionalTypes())) + break; + // HACK: Fix problem related to Swift 4 mode (with assertions), // since Swift 4 mode allows passing arguments with extra parens // to parameters which don't expect them, it should be supported @@ -6853,8 +6860,16 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, } case ConversionRestrictionKind::CGFloatToDouble: - case ConversionRestrictionKind::DoubleToCGFloat: { + case ConversionRestrictionKind::DoubleToCGFloat: + case ConversionRestrictionKind::ImplicitConversion: { auto conversionKind = knownRestriction->second; + ConstructorDecl *implicitConstructor = nullptr; + Identifier label; + if (conversionKind == ConversionRestrictionKind::ImplicitConversion) { +// fromType->dump(); toType->dump(); + implicitConstructor = cs.implicitConversionAvailable(fromType, toType); + label = cs.getASTContext().Id_implicit; + } auto *argExpr = locator.trySimplifyToExpr(); assert(argExpr); @@ -6865,7 +6880,7 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, auto *implicitInit = CallExpr::createImplicit(ctx, TypeExpr::createImplicit(toType, ctx), /*args=*/{argExpr}, - /*argLabels=*/{Identifier()}); + /*argLabels=*/{label}); cs.cacheExprTypes(implicitInit->getFn()); cs.setType(argExpr, fromType); @@ -6894,7 +6909,15 @@ Expr *ExprRewriter::coerceToType(Expr *expr, Type toType, ConstraintLocator::ApplyFunction, ConstraintLocator::ConstructorMember})); - solution.overloadChoices.insert({memberLoc, overload}); + if (implicitConstructor) { // Hack overload to constructor for implicits + SelectedOverload implicitOverload = { + OverloadChoice(toType, implicitConstructor, + overload.choice.getFunctionRefKind()), + overload.openedFullType, overload.openedType, overload.boundType}; + solution.overloadChoices.insert({memberLoc, implicitOverload}); + } + else + solution.overloadChoices.insert({memberLoc, overload}); } // Record the implicit call's parameter bindings and match direction. diff --git a/lib/Sema/CSDiagnostics.cpp b/lib/Sema/CSDiagnostics.cpp index 5848d4346114c..dbd3d1689af4c 100644 --- a/lib/Sema/CSDiagnostics.cpp +++ b/lib/Sema/CSDiagnostics.cpp @@ -6703,6 +6703,7 @@ void NonEphemeralConversionFailure::emitSuggestionNotes() const { case ConversionRestrictionKind::ObjCTollFreeBridgeToCF: case ConversionRestrictionKind::CGFloatToDouble: case ConversionRestrictionKind::DoubleToCGFloat: + case ConversionRestrictionKind::ImplicitConversion: llvm_unreachable("Expected an ephemeral conversion!"); } } diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index 76d5c1cac0bfe..c4f0e5dcac633 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -3635,6 +3635,10 @@ static bool repairOutOfOrderArgumentsInBinaryFunction( return false; } +/// Switch between restrictions mechanism and original PR using +/// ConversionRestrictionKind::PointerToPointer in repairFailures(). +static bool useRestrictions = true; + /// Attempt to repair typing failures and record fixes if needed. /// \return true if at least some of the failures has been repaired /// successfully, which allows type matcher to continue. @@ -4971,9 +4975,90 @@ bool ConstraintSystem::repairFailures( break; } + // Accept mutable pointers in the place of immutables. + if (implicitConversionAvailable(lhs, rhs)) + conversionsOrFixes.push_back( + ConversionRestrictionKind::ImplicitConversion); + return !conversionsOrFixes.empty(); } +ConstructorDecl *ConstraintSystem::implicitConversionAvailable(Type fromType, Type toType) { +#if 01 // Determine implicit conversions from init([_] implicit:) constructors + static int called = 0; + static const char *from = 0 ? "\nFROM" : nullptr; + auto trace = [](const char *prefix, Type ty) { + if (!from) + return; + if (prefix == from) + called++; + fprintf(stderr, prefix == from ? "%s %p #%d " : "%s %p ", + prefix, ty.getPointer(), called); + ty->dump(); + }; + trace(from, fromType); + trace("TO", toType); + if (NominalTypeDecl *toNominal = toType->getAnyNominal()) { + auto &ctx = getASTContext(); + if (!toNominal->setConversionsComputed()) + for (ExtensionDecl *extension : toNominal->getExtensions()) { + Type extType = extension->getExtendedType()->getCanonicalType(); + int arged = 0; + for (Decl *member : extension->getMembers()) + if (ConstructorDecl *initDecl = dyn_cast(member)) { + ParameterList *parameters = initDecl->getParameters(); + ParamDecl *param; // could be @implicit instead.. + if (parameters->size() == 1 && (param = parameters->get(0)) && + (param->getArgumentName() == ctx.Id_implicit || + param->getParameterName() == ctx.Id_implicit)) { + Type argType = param->getType()->getCanonicalType(); + if (!arged++) + trace("\nEXT", extType); + trace(" ARG", argType); + (*ctx.implicitConversionsTo(extType->getAnyNominal(), + /*create*/true))[argType->getAnyNominal()].push_back(initDecl); + } + } + } + + if (auto *exists = ctx.implicitConversionsTo(toNominal, /*create*/false)) + for (ConstructorDecl *initDecl : (*exists)[fromType->getAnyNominal()]) + if (ExtensionDecl *ext = dyn_cast(initDecl->getParent())) + if (Type argType = initDecl->getParameters()->get(0)->getType()) + if (Type extType = initDecl->getResultInterfaceType()) { + extType = initDecl->getParent()->mapTypeIntoContext(extType); + // More rigourous check of conversion here... + trace("EXT2", extType); + trace("ARG2", argType); + if (argType->getCanonicalType() == fromType->getCanonicalType() || + (extType->isUnsafeRawPointer() && + (fromType->isUnsafeMutablePointer() || fromType->isUnsafePointer()))) { + trace("SELECTED", initDecl->getInterfaceType()); + return initDecl; + } + } + } +#else // Original hard coded rules for unsafe pointers. + if (toType->isUnsafeRawPointer() && (fromType->isUnsafeMutableRawPointer() || + fromType->isUnsafeMutablePointer() || fromType->isUnsafePointer())) { + return true; // Unsafe[Mutable][Raw]Pointer -> UnsafeRawPointer + } + + auto firstGenericArgument = [&](Type ty) -> CanType { + return dyn_cast(ty->getCanonicalType()) + ->getGenericArgs()[0]->getCanonicalType(); + }; + + if (fromType->isUnsafeMutablePointer() && toType->isUnsafePointer() && + firstGenericArgument(fromType) == firstGenericArgument(toType)) { + toType->dump(); + fromType->dump(); + return true; // UnsafeMutablePointer -> UnsafePointer + } +#endif + return nullptr; +} + ConstraintSystem::TypeMatchResult ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, TypeMatchOptions flags, @@ -5317,10 +5402,12 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, } } - if (kind >= ConstraintKind::Subtype && +#if 0 + if (0 && kind >= ConstraintKind::Subtype && nominal1->getDecl() != nominal2->getDecl() && ((nominal1->isCGFloatType() || nominal2->isCGFloatType()) && - (nominal1->isDouble() || nominal2->isDouble()))) { + (nominal1->isDouble() || nominal2->isDouble())) || + useRestrictions && implicitConversionAvailable(type1, type2)) { ConstraintLocatorBuilder location{locator}; // Look through all value-to-optional promotions to allow // conversions like Double -> CGFloat?? and vice versa. @@ -5388,16 +5475,20 @@ ConstraintSystem::matchTypes(Type type1, Type type2, ConstraintKind kind, rawElt.getAs()) { auto convKind = elt->getConversionKind(); return convKind == ConversionRestrictionKind::DoubleToCGFloat || - convKind == ConversionRestrictionKind::CGFloatToDouble; + convKind == ConversionRestrictionKind::CGFloatToDouble || + convKind == ConversionRestrictionKind::ImplicitConversion; } return false; })) { conversionsOrFixes.push_back( - desugar1->isCGFloatType() - ? ConversionRestrictionKind::CGFloatToDouble - : ConversionRestrictionKind::DoubleToCGFloat); +// desugar1->isCGFloatType() +// ? ConversionRestrictionKind::CGFloatToDouble : +// desugar2->isCGFloatType() +// ? ConversionRestrictionKind::DoubleToCGFloat : + ConversionRestrictionKind::ImplicitConversion); } } +#endif break; } @@ -11123,7 +11214,8 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( } case ConversionRestrictionKind::DoubleToCGFloat: - case ConversionRestrictionKind::CGFloatToDouble: { + case ConversionRestrictionKind::CGFloatToDouble: + case ConversionRestrictionKind::ImplicitConversion: { // Prefer CGFloat -> Double over other way araund. auto impact = restriction == ConversionRestrictionKind::CGFloatToDouble ? 1 : 10; @@ -11170,8 +11262,11 @@ ConstraintSystem::simplifyRestrictedConstraintImpl( memberTy, DC, FunctionRefKind::DoubleApply, /*outerAlternatives=*/{}, memberLoc); + Identifier label; + if (restriction == ConversionRestrictionKind::ImplicitConversion) + label = getASTContext().Id_implicit; addConstraint(ConstraintKind::ApplicableFunction, - FunctionType::get({FunctionType::Param(type1)}, type2), + FunctionType::get({FunctionType::Param(type1, label)}, type2), memberTy, applicationLoc); ImplicitValueConversions.push_back( diff --git a/lib/Sema/Constraint.cpp b/lib/Sema/Constraint.cpp index 551fcc5f84281..622729eff6edd 100644 --- a/lib/Sema/Constraint.cpp +++ b/lib/Sema/Constraint.cpp @@ -562,6 +562,8 @@ StringRef swift::constraints::getName(ConversionRestrictionKind kind) { return "[CGFloat-to-Double]"; case ConversionRestrictionKind::DoubleToCGFloat: return "[Double-to-CGFloat]"; + case ConversionRestrictionKind::ImplicitConversion: + return "[Implicit-Conversion]"; } llvm_unreachable("bad conversion restriction kind"); } diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index 94c4c5c9235b4..2917166ee1449 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -183,6 +183,9 @@ void ConstraintSystem::assignFixedType(TypeVariableType *typeVar, Type type, getASTContext(), castToExpr(locator->getAnchor())); if (!literalProtocol) continue; +// fprintf(stderr, "HERE "); +// (locator->getAnchor()).dump(); +// fprintf(stderr, "\n"); // If the protocol has a default type, check it. if (auto defaultType = TypeChecker::getDefaultType(literalProtocol, DC)) { @@ -452,7 +455,8 @@ ConstraintLocator *ConstraintSystem::getCalleeLocator( if (auto conversion = locator->findLast()) { if (conversion->is(ConversionRestrictionKind::DoubleToCGFloat) || - conversion->is(ConversionRestrictionKind::CGFloatToDouble)) { + conversion->is(ConversionRestrictionKind::CGFloatToDouble) || + conversion->is(ConversionRestrictionKind::ImplicitConversion)) { return getConstraintLocator( ASTNode(), {*conversion, ConstraintLocator::ApplyFunction, ConstraintLocator::ConstructorMember}); @@ -5130,6 +5134,7 @@ ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion, case ConversionRestrictionKind::ObjCTollFreeBridgeToCF: case ConversionRestrictionKind::CGFloatToDouble: case ConversionRestrictionKind::DoubleToCGFloat: + case ConversionRestrictionKind::ImplicitConversion: // @_nonEphemeral has no effect on these conversions, so treat them as all // being non-ephemeral in order to allow their passing to an @_nonEphemeral // parameter. diff --git a/lib/Sema/TypeCheckCaptures.cpp b/lib/Sema/TypeCheckCaptures.cpp index fb60113b4848b..ce2b6533e253b 100644 --- a/lib/Sema/TypeCheckCaptures.cpp +++ b/lib/Sema/TypeCheckCaptures.cpp @@ -465,6 +465,10 @@ class FindCapturedVars : public ASTWalker { // Approximate this for the purposes of being able to invoke @objc methods // by considering tuples of ObjC-representable types to not use metadata. if (auto tuple = dyn_cast(E)) { + if (!tuple->getType()->is()) { + tuple->getType()->dump(); + return false; + } for (auto elt : tuple->getType()->castTo()->getElements()) { if (!elt.getType()->isRepresentableIn(ForeignLanguage::ObjectiveC, CurDC)) diff --git a/test/TypeCoercion/immutable_mutable.swift b/test/TypeCoercion/immutable_mutable.swift new file mode 100644 index 0000000000000..89e9976690aa7 --- /dev/null +++ b/test/TypeCoercion/immutable_mutable.swift @@ -0,0 +1,137 @@ +// RUN: %target-typecheck-verify-swift -parse-stdlib + +import Swift +import Foundation + +extension Double { + init(implicit: CGFloat) { + print("Double.init(implicit: CGFloat)") + self.init(implicit) + } +} +extension CGFloat { + init(implicit: Double) { + print("CGFloat.init(implicit: Double)") + self.init(implicit) + } +} +public struct III { + var i: Int +} +extension Int { + init(implicit: Int16) { + print("Int.init(implicit: Int16)") + self.init(implicit) + } + init(implicit: Int32) { + print("Int.init(implicit: Int32)") + self.init(implicit) + } + init(_ implicit: III) { + print("Int.init(implicit: I)") + self.init(implicit.i) + } +} +extension Int32 { + init(implicit: Int8) { + print("Int32.init(implicit: Int8)") + self.init(implicit) + } + init(implicit: Int16) { + print("Int32.init(implicit: Int16)") + self.init(implicit) + } +} +extension III { + public init(_ implicit: Int) { + print("III.init(implicit: Int)") + self.init(i: implicit) + } +} + +extension UnsafeRawPointer { + init(implicit: UnsafeMutableRawPointer) { + self.init(implicit) + } + init(implicit: UnsafeMutablePointer) { + self.init(implicit) + } + init(implicit: UnsafePointer) { + self.init(implicit) + } +} +extension UnsafePointer { + init(implicit: UnsafeMutablePointer) { + self.init(implicit) + } +} +extension UnsafePointer where Pointee == Int { + init(implicit: UnsafeMutablePointer) { + self.init(implicit) + } +} + +let x: Double = 99.1 +let y: CGFloat = x +let z: Double? = y + +let a: Int32 = 997 +let b: Int = a +let c: Int8 = 97 +let d: Int = c // expected-error {{cannot convert value of type 'Int8' to specified type 'Int'}} +let e = III(i: 888) +let f: Int = e +let h: Int16 = 45 +let j: Int32 = h + +let optionalMutableRawPointer = UnsafeMutableRawPointer(bitPattern: -3) +let mutableRawPointer = optionalMutableRawPointer! +let immutable: UnsafeRawPointer = mutableRawPointer +//let immutable2: UnsafeRawPointer? = optionalMutableRawPointer +let optionalImmutable: UnsafeRawPointer? = mutableRawPointer +let mutable: UnsafeMutableRawPointer = immutable // expected-error {{cannot convert value of type 'UnsafeRawPointer' to specified type 'UnsafeMutableRawPointer'}} + +if mutableRawPointer == immutable || immutable == mutableRawPointer || + mutableRawPointer == optionalImmutable || optionalImmutable == mutableRawPointer { +} + +func unmutable(immutable: UnsafeRawPointer) -> UnsafeRawPointer { + return mutableRawPointer +} +func unmutable(optionalImmutable: UnsafeRawPointer?) -> UnsafeRawPointer? { + return optionalImmutable//optionalMutableRawPointer +} + +_ = unmutable(immutable: mutableRawPointer) +_ = unmutable(optionalImmutable: mutableRawPointer) +_ = unmutable(optionalImmutable: optionalMutableRawPointer) + +var i = 99 +let mutable3 = UnsafeMutablePointer(&i) +let immutable3 = UnsafePointer(&i) +let immutable4: UnsafePointer = mutable3 +let immutable5: UnsafePointer? = mutable3 +let immutable6: UnsafePointer = mutable3 // expected-error {{cannot convert value of type 'UnsafeMutablePointer' to specified type 'UnsafePointer'}} +let mutable4: UnsafeMutablePointer = immutable3 // expected-error {{cannot convert value of type 'UnsafePointer' to specified type 'UnsafeMutablePointer'}} +if mutable3 == immutable3 || immutable3 == mutable3 || + immutable == immutable3 || immutable == mutable3 { +} + +func demutable(immutable: UnsafePointer) -> UnsafeRawPointer { + return mutableRawPointer +} +func demutable(optionalImmutable: UnsafePointer?) -> UnsafeRawPointer? { + return optionalImmutable//optionalMutableRawPointer +} + +_ = demutable(immutable: mutable3) +_ = demutable(optionalImmutable: mutable3) + +_ = unmutable(immutable: mutable3) +_ = unmutable(optionalImmutable: mutable3) + +var array = [1] +array.withUnsafeMutableBufferPointer { + _ = demutable(immutable: $0.baseAddress!) + _ = demutable(optionalImmutable: $0.baseAddress) +} diff --git a/test/decl/subscript/addressors.swift b/test/decl/subscript/addressors.swift index e048638f3e085..e86074e3231e5 100644 --- a/test/decl/subscript/addressors.swift +++ b/test/decl/subscript/addressors.swift @@ -46,7 +46,7 @@ struct Repeated { return UnsafePointer(base) } unsafeAddress { // expected-error {{subscript already has an addressor}} - return base // expected-error {{cannot convert return expression of type 'UnsafeMutablePointer' to return type 'UnsafePointer'}} + return base } } }