diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index dab96922984a8..fe7a502c5814b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -6856,7 +6856,13 @@ Type ConstructorDecl::getInitializerInterfaceType() { return InitializerInterfaceType; // Lazily calculate initializer type. - auto funcTy = getInterfaceType()->castTo()->getResult(); + auto allocatorTy = getInterfaceType(); + if (!allocatorTy->is()) { + InitializerInterfaceType = ErrorType::get(getASTContext()); + return InitializerInterfaceType; + } + + auto funcTy = allocatorTy->castTo()->getResult(); assert(funcTy->is()); // Constructors have an initializer type that takes an instance diff --git a/lib/SILGen/SILGenExpr.cpp b/lib/SILGen/SILGenExpr.cpp index 44faed1b4aec0..2638c022e1fa5 100644 --- a/lib/SILGen/SILGenExpr.cpp +++ b/lib/SILGen/SILGenExpr.cpp @@ -931,6 +931,25 @@ RValue RValueEmitter::visitOtherConstructorDeclRefExpr( } RValue RValueEmitter::visitNilLiteralExpr(NilLiteralExpr *E, SGFContext C) { + // Peephole away the call to Optional(nilLiteral: ()). + if (E->getType()->getOptionalObjectType()) { + auto *noneDecl = SGF.getASTContext().getOptionalNoneDecl(); + auto enumTy = SGF.getLoweredType(E->getType()); + + ManagedValue noneValue; + if (enumTy.isLoadable(SGF.F) || !SGF.silConv.useLoweredAddresses()) { + noneValue = ManagedValue::forUnmanaged( + SGF.B.createEnum(E, SILValue(), noneDecl, enumTy)); + } else { + noneValue = + SGF.B.bufferForExpr(E, enumTy, SGF.getTypeLowering(enumTy), C, + [&](SILValue newAddr) { + SGF.B.createInjectEnumAddr(E, newAddr, noneDecl); + }); + } + return RValue(SGF, E, noneValue); + } + return SGF.emitLiteral(E, C); } diff --git a/lib/Sema/CSApply.cpp b/lib/Sema/CSApply.cpp index 8b298af36413c..4f7abbced1fa5 100644 --- a/lib/Sema/CSApply.cpp +++ b/lib/Sema/CSApply.cpp @@ -1912,57 +1912,27 @@ namespace { } Expr *visitNilLiteralExpr(NilLiteralExpr *expr) { + auto type = simplifyType(cs.getType(expr)); + + // By far the most common 'nil' literal is for Optional.none. + // We don't have to look up the witness in this case since SILGen + // knows how to lower it directly. + if (auto objectType = type->getOptionalObjectType()) { + cs.setType(expr, type); + return expr; + } + auto &tc = cs.getTypeChecker(); auto *protocol = tc.getProtocol(expr->getLoc(), KnownProtocolKind::ExpressibleByNilLiteral); // For type-sugar reasons, prefer the spelling of the default literal // type. - auto type = simplifyType(cs.getType(expr)); if (auto defaultType = tc.getDefaultType(protocol, dc)) { if (defaultType->isEqual(type)) type = defaultType; } - // By far the most common 'nil' literal is for Optional.none. - // - // Emit this case directly instead of calling Optional.init(nilLiteral:), - // since this generates more efficient SIL. - if (auto objectType = type->getOptionalObjectType()) { - auto *nilDecl = tc.Context.getOptionalNoneDecl(); - tc.validateDecl(nilDecl); - if (!nilDecl->hasInterfaceType()) - return nullptr; - - auto genericSig = - nilDecl->getDeclContext()->getGenericSignatureOfContext(); - SubstitutionMap subs = - SubstitutionMap::get(genericSig, llvm::makeArrayRef(objectType), - { }); - ConcreteDeclRef concreteDeclRef(nilDecl, subs); - - auto nilType = FunctionType::get( - {FunctionType::Param(MetatypeType::get(type))}, type); - auto *nilRefExpr = new (tc.Context) DeclRefExpr( - concreteDeclRef, DeclNameLoc(expr->getLoc()), - /*implicit=*/true, AccessSemantics::Ordinary, - nilType); - cs.cacheType(nilRefExpr); - - auto *typeExpr = TypeExpr::createImplicitHack( - expr->getLoc(), - type, - tc.Context); - cs.cacheType(typeExpr); - - auto *callExpr = new (tc.Context) DotSyntaxCallExpr( - nilRefExpr, expr->getLoc(), typeExpr, type); - callExpr->setImplicit(true); - cs.cacheType(callExpr); - - return callExpr; - } - DeclName initName(tc.Context, DeclBaseName::createConstructor(), { tc.Context.Id_nilLiteral }); return convertLiteralInPlace(expr, type, protocol, diff --git a/lib/Sema/CodeSynthesis.cpp b/lib/Sema/CodeSynthesis.cpp index 0264c4e29a3df..cb200262ab0e4 100644 --- a/lib/Sema/CodeSynthesis.cpp +++ b/lib/Sema/CodeSynthesis.cpp @@ -441,25 +441,42 @@ createModifyCoroutinePrototype(AbstractStorageDecl *storage, /// Build an expression that evaluates the specified parameter list as a tuple /// or paren expr, suitable for use in an apply expr. static Expr *buildArgumentForwardingExpr(ArrayRef params, - ASTContext &ctx) { + ASTContext &ctx, + bool typeChecked) { SmallVector labels; SmallVector labelLocs; SmallVector args; - + SmallVector elts; + for (auto param : params) { + auto type = (typeChecked ? param->getType() : Type()); + + if (typeChecked) + elts.push_back(param->toFunctionParam(type)); + Expr *ref = new (ctx) DeclRefExpr(param, DeclNameLoc(), /*implicit*/ true); - if (param->isInOut()) + + if (typeChecked) + ref->setType(param->isInOut() ? LValueType::get(type) : type); + + if (param->isInOut()) { ref = new (ctx) InOutExpr(SourceLoc(), ref, Type(), /*isImplicit=*/true); - else if (param->isVariadic()) + if (typeChecked) + ref->setType(InOutType::get(type)); + } else if (param->isVariadic()) { ref = new (ctx) VarargExpansionExpr(ref, /*implicit*/ true); - else if (param->isAutoClosure()) { - // If parameter is marked as `@autoclosure` it means - // that it has to be called. - auto arg = TupleExpr::createEmpty(ctx, SourceLoc(), SourceLoc(), - /*implicit=*/true); - ref = CallExpr::create(ctx, ref, arg, {}, {}, - /*hasTrailingClosure=*/false, - /*implicit=*/true); + if (typeChecked) + ref->setType(type); + } else if (param->isAutoClosure()) { + if (!typeChecked) { + // If parameter is marked as `@autoclosure` it means + // that it has to be called. + auto arg = TupleExpr::createEmpty(ctx, SourceLoc(), SourceLoc(), + /*implicit=*/true); + ref = CallExpr::create(ctx, ref, arg, {}, {}, + /*hasTrailingClosure=*/false, + /*implicit=*/true); + } } args.push_back(ref); @@ -467,23 +484,33 @@ static Expr *buildArgumentForwardingExpr(ArrayRef params, labels.push_back(param->getArgumentName()); labelLocs.push_back(SourceLoc()); } - - // A single unlabeled value is not a tuple. - if (args.size() == 1 && labels[0].empty() && + + Expr *argExpr; + if (args.size() == 1 && + labels[0].empty() && !isa(args[0])) { - return new (ctx) ParenExpr(SourceLoc(), args[0], SourceLoc(), - /*hasTrailingClosure=*/false); + argExpr = new (ctx) ParenExpr(SourceLoc(), args[0], SourceLoc(), + /*hasTrailingClosure=*/false); + argExpr->setImplicit(); + } else { + argExpr = TupleExpr::create(ctx, SourceLoc(), args, labels, labelLocs, + SourceLoc(), false, IsImplicit); } - - return TupleExpr::create(ctx, SourceLoc(), args, labels, labelLocs, - SourceLoc(), false, IsImplicit); + + if (typeChecked) { + auto argTy = AnyFunctionType::composeInput(ctx, elts, /*canonical*/false); + argExpr->setType(argTy); + } + + return argExpr; } /// Build a reference to the subscript index variables for this subscript /// accessor. static Expr *buildSubscriptIndexReference(ASTContext &ctx, - AccessorDecl *accessor) { + AccessorDecl *accessor, + bool typeChecked) { // Pull out the body parameters, which we should have cloned // previously to be forwardable. Drop the initial buffer/value // parameter in accessors that have one. @@ -496,9 +523,7 @@ static Expr *buildSubscriptIndexReference(ASTContext &ctx, } // Okay, everything else should be forwarded, build the expression. - auto result = buildArgumentForwardingExpr(params, ctx); - assert(result && "FIXME: Cannot forward expression"); - return result; + return buildArgumentForwardingExpr(params, ctx, typeChecked); } enum class SelfAccessorKind { @@ -514,13 +539,24 @@ enum class SelfAccessorKind { static Expr *buildSelfReference(VarDecl *selfDecl, SelfAccessorKind selfAccessorKind, - ASTContext &ctx) { + bool isLValue, + ASTContext &ctx, + bool typeChecked) { switch (selfAccessorKind) { case SelfAccessorKind::Peer: - return new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), IsImplicit); + return new (ctx) DeclRefExpr(selfDecl, DeclNameLoc(), IsImplicit, + AccessSemantics::Ordinary, + typeChecked ? + (isLValue + ? LValueType::get(selfDecl->getType()) + : selfDecl->getType()) : Type()); case SelfAccessorKind::Super: - return new (ctx) SuperRefExpr(selfDecl, SourceLoc(), IsImplicit); + assert(!isLValue); + return new (ctx) SuperRefExpr(selfDecl, SourceLoc(), IsImplicit, + typeChecked + ? selfDecl->getType()->getSuperclass() + : Type()); } llvm_unreachable("bad self access kind"); } @@ -549,7 +585,9 @@ namespace { static Expr *buildStorageReference(AccessorDecl *accessor, AbstractStorageDecl *storage, TargetImpl target, - ASTContext &ctx) { + bool isLValue, + ASTContext &ctx, + bool typeChecked) { // Local function to "finish" the expression, creating a member reference // to the underlying variable if there is one. VarDecl *underlyingVar = nullptr; @@ -557,12 +595,40 @@ static Expr *buildStorageReference(AccessorDecl *accessor, if (!underlyingVar) return result; - return new (ctx) MemberRefExpr( - result, SourceLoc(), underlyingVar, DeclNameLoc(), /*Implicit=*/true); + SubstitutionMap subs; + if (typeChecked) { + subs = result->getType() + ->getWithoutSpecifierType() + ->getContextSubstitutionMap( + accessor->getParentModule(), + underlyingVar->getDeclContext()); + } + + ConcreteDeclRef memberRef(underlyingVar, subs); + auto *memberRefExpr = new (ctx) MemberRefExpr( + result, SourceLoc(), memberRef, DeclNameLoc(), /*Implicit=*/true); + if (typeChecked) { + auto type = underlyingVar->getValueInterfaceType() + .subst(subs, SubstFlags::UseErrorType); + if (isLValue) + type = LValueType::get(type); + memberRefExpr->setType(type); + } + + return memberRefExpr; }; + VarDecl *selfDecl = accessor->getImplicitSelfDecl(); + AccessSemantics semantics; SelfAccessorKind selfAccessKind; + Type selfTypeForAccess = (selfDecl ? selfDecl->getType() : Type()); + + auto *genericEnv = accessor->getGenericEnvironment(); + SubstitutionMap subs; + if (genericEnv && typeChecked) + subs = genericEnv->getForwardingSubstitutionMap(); + switch (target) { case TargetImpl::Ordinary: semantics = AccessSemantics::Ordinary; @@ -584,6 +650,16 @@ static Expr *buildStorageReference(AccessorDecl *accessor, if (auto override = storage->getOverriddenDecl()) { semantics = AccessSemantics::Ordinary; selfAccessKind = SelfAccessorKind::Super; + + if (typeChecked) { + auto *baseClass = override->getDeclContext()->getSelfClassDecl(); + selfTypeForAccess = selfTypeForAccess->getSuperclassForDecl(baseClass); + subs = + selfTypeForAccess->getContextSubstitutionMap( + accessor->getParentModule(), + baseClass); + } + storage = override; // Otherwise do a self-reference, which is dynamically bogus but @@ -616,24 +692,77 @@ static Expr *buildStorageReference(AccessorDecl *accessor, } } - VarDecl *selfDecl = accessor->getImplicitSelfDecl(); if (!selfDecl) { assert(target != TargetImpl::Super); - return finish( - new (ctx) DeclRefExpr(storage, DeclNameLoc(), IsImplicit, semantics)); + auto *storageDRE = new (ctx) DeclRefExpr(storage, DeclNameLoc(), + IsImplicit, semantics); + if (typeChecked) { + auto type = storage->getValueInterfaceType() + .subst(subs, SubstFlags::UseErrorType); + if (isLValue) + type = LValueType::get(type); + storageDRE->setType(type); + } + return finish(storageDRE); + } + + bool isMemberLValue = false; + if (typeChecked) { + // If we're acessing a property delegate, determine if the + // intermediate access requires an lvalue. + if (underlyingVar) { + isMemberLValue = underlyingVar->isGetterMutating(); + if (isLValue) + isMemberLValue |= underlyingVar->isSetterMutating(); + } else { + isMemberLValue = isLValue; + } + } + + bool isSelfLValue = false; + if (typeChecked) { + isSelfLValue = storage->isGetterMutating(); + if (isMemberLValue) + isSelfLValue |= storage->isSetterMutating(); } Expr *selfDRE = - buildSelfReference(selfDecl, selfAccessKind, ctx); + buildSelfReference(selfDecl, selfAccessKind, isSelfLValue, + ctx, typeChecked); + if (typeChecked) { + if (isSelfLValue) + selfTypeForAccess = LValueType::get(selfTypeForAccess); + + if (!selfDRE->getType()->isEqual(selfTypeForAccess)) { + assert(selfAccessKind == SelfAccessorKind::Super); + selfDRE = new (ctx) DerivedToBaseExpr(selfDRE, selfTypeForAccess); + } + } + + LookupExpr *lookupExpr; + ConcreteDeclRef memberRef(storage, subs); if (auto subscript = dyn_cast(storage)) { - Expr *indices = buildSubscriptIndexReference(ctx, accessor); - return finish(SubscriptExpr::create(ctx, selfDRE, indices, storage, - IsImplicit, semantics)); + Expr *indices = buildSubscriptIndexReference(ctx, accessor, typeChecked); + lookupExpr = SubscriptExpr::create(ctx, selfDRE, indices, memberRef, + IsImplicit, semantics); + } else { + lookupExpr = new (ctx) MemberRefExpr(selfDRE, SourceLoc(), memberRef, + DeclNameLoc(), IsImplicit, semantics); } - return finish(new (ctx) MemberRefExpr(selfDRE, SourceLoc(), storage, - DeclNameLoc(), IsImplicit, semantics)); + if (typeChecked) { + if (selfAccessKind == SelfAccessorKind::Super) + lookupExpr->setIsSuper(true); + + auto type = storage->getValueInterfaceType() + .subst(subs, SubstFlags::UseErrorType); + if (isMemberLValue) + type = LValueType::get(type); + lookupExpr->setType(type); + } + + return finish(lookupExpr); } /// Load the value of VD. If VD is an @override of another value, we call the @@ -642,8 +771,10 @@ static Expr * createPropertyLoadOrCallSuperclassGetter(AccessorDecl *accessor, AbstractStorageDecl *storage, TargetImpl target, - ASTContext &ctx) { - return buildStorageReference(accessor, storage, target, ctx); + ASTContext &ctx, + bool typeChecked) { + return buildStorageReference(accessor, storage, target, /*isLValue=*/false, + ctx, typeChecked); } /// Look up the NSCopying protocol from the Foundation module, if present. @@ -666,16 +797,19 @@ static ProtocolDecl *getNSCopyingProtocol(ASTContext &ctx, return dyn_cast(results.front()); } -static bool checkConformanceToNSCopying(ASTContext &ctx, VarDecl *var, - Type type) { +static Optional +checkConformanceToNSCopying(ASTContext &ctx, VarDecl *var, Type type) { auto dc = var->getDeclContext(); auto proto = getNSCopyingProtocol(ctx, dc); - if (!proto || !TypeChecker::conformsToProtocol(type, proto, dc, None)) { - ctx.Diags.diagnose(var->getLoc(), diag::nscopying_doesnt_conform); - return true; + if (proto) { + auto result = TypeChecker::conformsToProtocol(type, proto, dc, None); + if (result) + return result; } - return false; + + ctx.Diags.diagnose(var->getLoc(), diag::nscopying_doesnt_conform); + return None; } static std::pair getUnderlyingTypeOfVariable(VarDecl *var) { @@ -688,7 +822,8 @@ static std::pair getUnderlyingTypeOfVariable(VarDecl *var) { } } -bool TypeChecker::checkConformanceToNSCopying(VarDecl *var) { +Optional +TypeChecker::checkConformanceToNSCopying(VarDecl *var) { Type type = getUnderlyingTypeOfVariable(var).first; return ::checkConformanceToNSCopying(Context, var, type); } @@ -698,7 +833,8 @@ bool TypeChecker::checkConformanceToNSCopying(VarDecl *var) { /// just need to generate something like "self.property = val.copy(zone: nil)" /// here. This does some type checking to validate that the call will succeed. static Expr *synthesizeCopyWithZoneCall(Expr *Val, VarDecl *VD, - ASTContext &Ctx) { + ASTContext &Ctx, + bool typeChecked) { // We support @NSCopying on class types (which conform to NSCopying), // protocols which conform, and option types thereof. auto underlyingTypeAndIsOptional = getUnderlyingTypeOfVariable(VD); @@ -707,49 +843,94 @@ static Expr *synthesizeCopyWithZoneCall(Expr *Val, VarDecl *VD, // The element type must conform to NSCopying. If not, emit an error and just // recovery by synthesizing without the copy call. - if (checkConformanceToNSCopying(Ctx, VD, underlyingType)) { + auto conformance = checkConformanceToNSCopying(Ctx, VD, underlyingType); + if (!conformance) return Val; + + //- (id)copyWithZone:(NSZone *)zone; + DeclName copyWithZoneName(Ctx, Ctx.getIdentifier("copy"), { Ctx.Id_with }); + FuncDecl *copyMethod = nullptr; + for (auto member : conformance->getRequirement()->getMembers()) { + if (auto func = dyn_cast(member)) { + if (func->getFullName() == copyWithZoneName) { + copyMethod = func; + break; + } + } } + assert(copyMethod != nullptr); // If we have an optional type, we have to "?" the incoming value to only // evaluate the subexpression if the incoming value is non-null. - if (isOptional) + if (isOptional) { Val = new (Ctx) BindOptionalExpr(Val, SourceLoc(), 0); + if (typeChecked) + Val->setType(underlyingType); + } + + SubstitutionMap subs = + SubstitutionMap::get(copyMethod->getGenericSignature(), + {underlyingType}, + ArrayRef(*conformance)); + ConcreteDeclRef copyMethodRef(copyMethod, subs); + auto copyMethodType = copyMethod->getInterfaceType() + ->castTo() + ->substGenericArgs(subs); + auto DRE = new (Ctx) DeclRefExpr(copyMethodRef, DeclNameLoc(), IsImplicit); + if (typeChecked) + DRE->setType(copyMethodType); + + // Drop the self type + copyMethodType = copyMethodType->getResult()->castTo(); + + auto DSCE = new (Ctx) DotSyntaxCallExpr(DRE, SourceLoc(), Val); + DSCE->setImplicit(); + if (typeChecked) + DSCE->setType(copyMethodType); - // Generate: - // (force_value_expr type='' - // (call_expr type='' - // (unresolved_dot_expr type='' field 'copy' - // "Val") - // (paren_expr type='' - // (nil_literal_expr type='')))) - auto UDE = new (Ctx) UnresolvedDotExpr(Val, SourceLoc(), - Ctx.getIdentifier("copy"), - DeclNameLoc(), /*implicit*/true); Expr *Nil = new (Ctx) NilLiteralExpr(SourceLoc(), /*implicit*/true); + if (typeChecked) + Nil->setType(copyMethodType->getParams()[0].getParameterType()); - //- (id)copyWithZone:(NSZone *)zone; - Expr *Call = CallExpr::createImplicit(Ctx, UDE, { Nil }, { Ctx.Id_with }); + Expr *Call = CallExpr::createImplicit(Ctx, DSCE, { Nil }, { Ctx.Id_with }); + if (typeChecked) + Call->setType(copyMethodType->getResult()); TypeLoc ResultTy; ResultTy.setType(VD->getType()); // If we're working with non-optional types, we're forcing the cast. if (!isOptional) { - Call = new (Ctx) ForcedCheckedCastExpr(Call, SourceLoc(), SourceLoc(), - TypeLoc::withoutLoc(underlyingType)); - Call->setImplicit(); - return Call; + auto *Cast = + new (Ctx) ForcedCheckedCastExpr(Call, SourceLoc(), SourceLoc(), + TypeLoc::withoutLoc(underlyingType)); + Cast->setImplicit(); + if (typeChecked) { + Cast->setCastKind(CheckedCastKind::ValueCast); + Cast->setType(underlyingType); + } + + return Cast; } // We're working with optional types, so perform a conditional checked // downcast. - Call = new (Ctx) ConditionalCheckedCastExpr(Call, SourceLoc(), SourceLoc(), - TypeLoc::withoutLoc(underlyingType)); - Call->setImplicit(); + auto *Cast = + new (Ctx) ConditionalCheckedCastExpr(Call, SourceLoc(), SourceLoc(), + TypeLoc::withoutLoc(underlyingType)); + if (typeChecked) { + Cast->setCastKind(CheckedCastKind::ValueCast); + Cast->setType(OptionalType::get(underlyingType)); + } + + Cast->setImplicit(); // Use OptionalEvaluationExpr to evaluate the "?". - return new (Ctx) OptionalEvaluationExpr(Call); + auto *Result = new (Ctx) OptionalEvaluationExpr(Cast); + if (typeChecked) + Result->setType(OptionalType::get(underlyingType)); + + return Result; } /// In a synthesized accessor body, store 'value' to the appropriate element. @@ -762,22 +943,35 @@ void createPropertyStoreOrCallSuperclassSetter(AccessorDecl *accessor, AbstractStorageDecl *storage, TargetImpl target, SmallVectorImpl &body, - ASTContext &ctx) { + ASTContext &ctx, + bool typeChecked) { // If the storage is an @NSCopying property, then we store the // result of a copyWithZone call on the value, not the value itself. if (auto property = dyn_cast(storage)) { if (property->getAttrs().hasAttribute()) - value = synthesizeCopyWithZoneCall(value, property, ctx); + value = synthesizeCopyWithZoneCall(value, property, ctx, typeChecked); } - // Create: - // (assign (decl_ref_expr(VD)), decl_ref_expr(value)) - // or: - // (assign (member_ref_expr(decl_ref_expr(self), VD)), decl_ref_expr(value)) - Expr *dest = buildStorageReference(accessor, storage, target, ctx); + Expr *dest = buildStorageReference(accessor, storage, target, + /*isLValue=*/true, + ctx, typeChecked); - body.push_back(new (ctx) AssignExpr(dest, SourceLoc(), value, - IsImplicit)); + // A lazy property setter will store a value of type T into underlying storage + // of type T?. + if (typeChecked) { + auto destType = dest->getType()->getWithoutSpecifierType(); + if (!destType->isEqual(value->getType())) { + assert(destType->getOptionalObjectType()->isEqual(value->getType())); + value = new (ctx) InjectIntoOptionalExpr(value, destType); + } + } + + auto *assign = new (ctx) AssignExpr(dest, SourceLoc(), value, + IsImplicit); + if (typeChecked) + assign->setType(ctx.TheEmptyTupleType); + + body.push_back(assign); } LLVM_ATTRIBUTE_UNUSED @@ -803,10 +997,11 @@ static void synthesizeTrivialGetterBody(AccessorDecl *getter, SourceLoc loc = storage->getLoc(); Expr *result = - createPropertyLoadOrCallSuperclassGetter(getter, storage, target, ctx); + createPropertyLoadOrCallSuperclassGetter(getter, storage, target, ctx, true); ASTNode returnStmt = new (ctx) ReturnStmt(SourceLoc(), result, IsImplicit); getter->setBody(BraceStmt::create(ctx, loc, returnStmt, loc, true)); + getter->setBodyTypeCheckedIfPresent(); maybeMarkTransparent(getter, ctx); } @@ -859,18 +1054,24 @@ static void synthesizeTrivialSetterBodyWithStorage(AccessorDecl *setter, TargetImpl target, AbstractStorageDecl *storageToUse, - ASTContext &ctx) { + ASTContext &ctx, + bool typeChecked) { SourceLoc loc = setter->getStorage()->getLoc(); VarDecl *valueParamDecl = getFirstParamDecl(setter); auto *valueDRE = new (ctx) DeclRefExpr(valueParamDecl, DeclNameLoc(), IsImplicit); + if (typeChecked) + valueDRE->setType(valueParamDecl->getType()); + SmallVector setterBody; createPropertyStoreOrCallSuperclassSetter(setter, valueDRE, storageToUse, - target, setterBody, ctx); + target, setterBody, ctx, typeChecked); setter->setBody(BraceStmt::create(ctx, loc, setterBody, loc, true)); + if (typeChecked) + setter->setBodyTypeCheckedIfPresent(); maybeMarkTransparent(setter, ctx); } @@ -879,8 +1080,9 @@ static void synthesizeTrivialSetterBody(AccessorDecl *setter, ASTContext &ctx) { auto storage = setter->getStorage(); assert(!isSynthesizedComputedProperty(storage)); + synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Storage, - storage, ctx); + storage, ctx, true); } /// Synthesize the body of a setter for a property delegate, which @@ -888,11 +1090,13 @@ static void synthesizeTrivialSetterBody(AccessorDecl *setter, static void synthesizePropertyDelegateSetterBody(AccessorDecl *setter, ASTContext &ctx) { synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Delegate, - setter->getStorage(), ctx); + setter->getStorage(), ctx, + true); } static void synthesizeCoroutineAccessorBody(AccessorDecl *accessor, - ASTContext &ctx) { + ASTContext &ctx, + bool typeChecked) { assert(accessor->isCoroutine()); auto storage = accessor->getStorage(); @@ -903,12 +1107,20 @@ static void synthesizeCoroutineAccessorBody(AccessorDecl *accessor, SourceLoc loc = storage->getLoc(); SmallVector body; + bool isLValue = accessor->getAccessorKind() == AccessorKind::Modify; + // Build a reference to the storage. - Expr *ref = buildStorageReference(accessor, storage, target, ctx); + Expr *ref = buildStorageReference(accessor, storage, target, isLValue, + ctx, typeChecked); // Wrap it with an `&` marker if this is a modify. - if (accessor->getAccessorKind() == AccessorKind::Modify) { - ref = new (ctx) InOutExpr(SourceLoc(), ref, Type(), true); + if (isLValue) { + auto *inoutRef = new (ctx) InOutExpr(SourceLoc(), ref, Type(), true); + if (typeChecked) { + auto type = ref->getType()->getWithoutSpecifierType(); + inoutRef->setType(InOutType::get(type)); + } + ref = inoutRef; } // Yield it. @@ -916,6 +1128,8 @@ static void synthesizeCoroutineAccessorBody(AccessorDecl *accessor, body.push_back(yield); accessor->setBody(BraceStmt::create(ctx, loc, body, loc, true)); + if (typeChecked) + accessor->setBodyTypeCheckedIfPresent(); maybeMarkTransparent(accessor, ctx); } @@ -924,7 +1138,7 @@ static void synthesizeCoroutineAccessorBody(AccessorDecl *accessor, static void synthesizeReadCoroutineBody(AccessorDecl *read, ASTContext &ctx) { assert(read->getStorage()->getReadImpl() != ReadImplKind::Read); - synthesizeCoroutineAccessorBody(read, ctx); + synthesizeCoroutineAccessorBody(read, ctx, true); } /// Synthesize the body of a modify coroutine. @@ -935,7 +1149,7 @@ static void synthesizeModifyCoroutineBody(AccessorDecl *modify, assert(impl != ReadWriteImplKind::Modify && impl != ReadWriteImplKind::Immutable); #endif - synthesizeCoroutineAccessorBody(modify, ctx); + synthesizeCoroutineAccessorBody(modify, ctx, true); } static void addGetterToStorage(AbstractStorageDecl *storage, @@ -1043,7 +1257,7 @@ static void synthesizeMutableAddressSetterBody(AccessorDecl *setter, ASTContext &ctx) { // This should call the mutable addressor. synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Implementation, - setter->getStorage(), ctx); + setter->getStorage(), ctx, true); } /// Synthesize the body of a setter which just delegates to a modify @@ -1052,7 +1266,7 @@ static void synthesizeModifyCoroutineSetterBody(AccessorDecl *setter, ASTContext &ctx) { // This should call the modify coroutine. synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Implementation, - setter->getStorage(), ctx); + setter->getStorage(), ctx, true); } static void convertNSManagedStoredVarToComputed(VarDecl *VD, ASTContext &ctx) { @@ -1153,7 +1367,7 @@ static void synthesizeObservedSetterBody(AccessorDecl *Set, VarDecl *OldValue = nullptr; if (VD->getDidSetFunc()) { Expr *OldValueExpr - = createPropertyLoadOrCallSuperclassGetter(Set, VD, target, Ctx); + = createPropertyLoadOrCallSuperclassGetter(Set, VD, target, Ctx, false); OldValue = new (Ctx) VarDecl(/*IsStatic*/false, VarDecl::Specifier::Let, /*IsCaptureList*/false, SourceLoc(), @@ -1188,7 +1402,7 @@ static void synthesizeObservedSetterBody(AccessorDecl *Set, // Create an assignment into the storage or call to superclass setter. auto *ValueDRE = new (Ctx) DeclRefExpr(ValueDecl, DeclNameLoc(), true); createPropertyStoreOrCallSuperclassSetter(Set, ValueDRE, VD, target, - SetterBody, Ctx); + SetterBody, Ctx, false); // Create: // (call_expr (dot_syntax_call_expr (decl_ref_expr(didSet)), @@ -1304,7 +1518,7 @@ static void synthesizeLazyGetterBody(AbstractFunctionDecl *fn, void *context) { auto *Tmp1PBDPattern = new (Ctx) NamedPattern(Tmp1VD, /*implicit*/true); auto *Tmp1Init = createPropertyLoadOrCallSuperclassGetter(Get, Storage, - TargetImpl::Storage, Ctx); + TargetImpl::Storage, Ctx, false); auto *Tmp1PBD = PatternBindingDecl::createImplicit( Ctx, StaticSpellingKind::None, Tmp1PBDPattern, Tmp1Init, Get); Body.push_back(Tmp1PBD); @@ -1382,7 +1596,7 @@ static void synthesizeLazyGetterBody(AbstractFunctionDecl *fn, void *context) { auto Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, DeclNameLoc(), /*Implicit*/true, AccessSemantics::DirectToStorage); createPropertyStoreOrCallSuperclassSetter(Get, Tmp2DRE, Storage, - TargetImpl::Storage, Body, Ctx); + TargetImpl::Storage, Body, Ctx, false); // Return tmp2. Tmp2DRE = new (Ctx) DeclRefExpr(Tmp2VD, DeclNameLoc(), /*Implicit*/true, @@ -1403,7 +1617,7 @@ static void synthesizeLazySetterBody(AbstractFunctionDecl *fn, void *context) { return; synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::Storage, - underlyingStorage, ctx); + underlyingStorage, ctx, true); } void swift::completeLazyVarImplementation(VarDecl *VD) { @@ -1964,7 +2178,7 @@ static void synthesizeSetterBody(AccessorDecl *setter, auto backingVar = original->getPropertyDelegateBackingProperty(); synthesizeTrivialSetterBodyWithStorage(setter, TargetImpl::DelegateStorage, - backingVar, ctx); + backingVar, ctx, true); return; } } @@ -2389,32 +2603,60 @@ static void synthesizeDesignatedInitOverride(AbstractFunctionDecl *fn, auto *ctor = cast(fn); auto &ctx = ctor->getASTContext(); - auto *bodyParams = ctor->getParameters(); auto *superclassCtor = (ConstructorDecl *) context; + if (!superclassCtor->hasValidSignature()) + ctx.getLazyResolver()->resolveDeclSignature(superclassCtor); + // Reference to super.init. auto *selfDecl = ctor->getImplicitSelfDecl(); - Expr *superRef = new (ctx) SuperRefExpr(selfDecl, SourceLoc(), - /*Implicit=*/true); - Expr *ctorRef = new (ctx) UnresolvedDotExpr(superRef, SourceLoc(), - superclassCtor->getFullName(), - DeclNameLoc(), - /*Implicit=*/true); - - auto ctorArgs = buildArgumentForwardingExpr(bodyParams->getArray(), ctx); + auto *superRef = buildSelfReference(selfDecl, SelfAccessorKind::Super, + /*isLValue=*/false, ctx, true); + + SubstitutionMap subs; + if (auto *genericEnv = fn->getGenericEnvironment()) + subs = genericEnv->getForwardingSubstitutionMap(); + subs = SubstitutionMap::getOverrideSubstitutions(superclassCtor, fn, subs); + ConcreteDeclRef ctorRef(superclassCtor, subs); + + auto type = superclassCtor->getInitializerInterfaceType() + .subst(subs, SubstFlags::UseErrorType); + auto *ctorRefExpr = + new (ctx) OtherConstructorDeclRefExpr(ctorRef, DeclNameLoc(), + IsImplicit, type); + + if (auto *funcTy = type->getAs()) + type = funcTy->getResult(); + auto *superclassCtorRefExpr = + new (ctx) DotSyntaxCallExpr(ctorRefExpr, SourceLoc(), superRef, type); + superclassCtorRefExpr->setIsSuper(true); - Expr *superCall = - CallExpr::create(ctx, ctorRef, ctorArgs, + auto *bodyParams = ctor->getParameters(); + auto ctorArgs = buildArgumentForwardingExpr(bodyParams->getArray(), ctx, true); + Expr *superclassCallExpr = + CallExpr::create(ctx, superclassCtorRefExpr, ctorArgs, superclassCtor->getFullName().getArgumentNames(), { }, /*hasTrailingClosure=*/false, /*implicit=*/true); + + if (auto *funcTy = type->getAs()) + type = funcTy->getResult(); + superclassCallExpr->setType(type); + if (superclassCtor->hasThrows()) { - superCall = new (ctx) TryExpr(SourceLoc(), superCall, Type(), - /*implicit=*/true); + superclassCallExpr = new (ctx) TryExpr(SourceLoc(), superclassCallExpr, + type, /*implicit=*/true); } - ctor->setBody(BraceStmt::create(ctx, SourceLoc(), - ASTNode(superCall), - SourceLoc(), + + auto *rebindSelfExpr = + new (ctx) RebindSelfInConstructorExpr(superclassCallExpr, + selfDecl); + + SmallVector stmts; + stmts.push_back(rebindSelfExpr); + stmts.push_back(new (ctx) ReturnStmt(SourceLoc(), /*Result=*/nullptr)); + ctor->setBody(BraceStmt::create(ctx, SourceLoc(), stmts, SourceLoc(), /*implicit=*/true)); + ctor->setBodyTypeCheckedIfPresent(); } ConstructorDecl * @@ -2464,7 +2706,7 @@ swift::createDesignatedInitOverride(TypeChecker &tc, // like 'class A : B'. for (auto *decl : *bodyParams) { auto paramTy = decl->getInterfaceType(); - auto substTy = paramTy.subst(subMap); + auto substTy = paramTy.subst(subMap, SubstFlags::UseErrorType); decl->setInterfaceType(substTy); } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index 1fb061d9d473d..cb0a4f34b58e9 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -1410,7 +1410,7 @@ void AttributeChecker::visitNSCopyingAttr(NSCopyingAttr *attr) { } if (VD->hasInterfaceType()) { - if (TC.checkConformanceToNSCopying(VD)) { + if (!TC.checkConformanceToNSCopying(VD)) { attr->setInvalid(); return; } diff --git a/lib/Sema/TypeCheckError.cpp b/lib/Sema/TypeCheckError.cpp index 97e5d4f7983e9..a6d82aaf8deaf 100644 --- a/lib/Sema/TypeCheckError.cpp +++ b/lib/Sema/TypeCheckError.cpp @@ -685,8 +685,13 @@ class ApplyClassifier { PotentialReason::forDefaultArgument()); } - // If this argument is `nil` literal or `.none`, - // it doesn't cause the call to throw. + // If this argument is `nil` literal, it doesn't cause the call to throw. + if (isa(arg)) { + if (arg->getType()->getOptionalObjectType()) + return Classification(); + } + + // Neither does 'Optional.none'. if (auto *DSCE = dyn_cast(arg)) { if (auto *DE = dyn_cast(DSCE->getFn())) { auto &ctx = paramType->getASTContext(); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index c063af5eaeda3..6aaec4ba625c3 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1883,6 +1883,9 @@ bool TypeChecker::typeCheckAbstractFunctionBody(AbstractFunctionDecl *AFD) { // HACK: don't type-check the same function body twice. This is // supposed to be handled by just not enqueuing things twice, // but that gets tricky with synthesized function bodies. + validateDecl(AFD); + (void) AFD->getBody(); + if (AFD->isBodyTypeChecked()) return false; diff --git a/lib/Sema/TypeChecker.h b/lib/Sema/TypeChecker.h index 0452ed43ea3f4..8a7749651035d 100644 --- a/lib/Sema/TypeChecker.h +++ b/lib/Sema/TypeChecker.h @@ -1688,9 +1688,7 @@ class TypeChecker final : public LazyResolver { IterableDeclContext *idc); /// Check that the type of the given property conforms to NSCopying. - /// - /// Return true if there was an error. - bool checkConformanceToNSCopying(VarDecl *var); + Optional checkConformanceToNSCopying(VarDecl *var); /// Mark any _BridgedNSError/_BridgedStoredNSError/related /// conformances in the given type as "used". diff --git a/test/SILGen/extensions.swift b/test/SILGen/extensions.swift index 39ac013a93d45..db5940e163534 100644 --- a/test/SILGen/extensions.swift +++ b/test/SILGen/extensions.swift @@ -44,7 +44,6 @@ func extensionMethodCurrying(_ x: Foo) { // CHECK-LABEL: sil hidden [transparent] [ossa] @$s10extensions3BoxV1txSgvpfi : $@convention(thin) () -> @out Optional // CHECK: bb0(%0 : $*Optional): -// CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin Optional.Type // CHECK: inject_enum_addr %0 : $*Optional, #Optional.none!enumelt // CHECK-NEXT: [[RESULT:%.*]] = tuple () // CHECK-NEXT: return [[RESULT]] : $() diff --git a/test/SILGen/objc_properties.swift b/test/SILGen/objc_properties.swift index db29e87e7a3ee..c39f7f8097dc0 100644 --- a/test/SILGen/objc_properties.swift +++ b/test/SILGen/objc_properties.swift @@ -106,7 +106,7 @@ class TestNSCopying { // CHECK-LABEL: sil hidden [transparent] [ossa] @$s15objc_properties13TestNSCopyingC8propertySo8NSStringCvs : $@convention(method) (@owned NSString, @guaranteed TestNSCopying) -> () // CHECK: bb0([[ARG0:%.*]] : @owned $NSString, [[ARG1:%.*]] : @guaranteed $TestNSCopying): // CHECK: [[BORROWED_ARG0:%.*]] = begin_borrow [[ARG0]] - // CHECK: objc_method [[BORROWED_ARG0]] : $NSString, #NSString.copy!1.foreign + // CHECK: objc_method [[BORROWED_ARG0]] : $NSString, #NSCopying.copy!1.foreign @NSCopying var property : NSString @NSCopying var optionalProperty : NSString? diff --git a/test/SILGen/observers.swift b/test/SILGen/observers.swift new file mode 100644 index 0000000000000..9f0c26e7feba4 --- /dev/null +++ b/test/SILGen/observers.swift @@ -0,0 +1,439 @@ + +// RUN: %target-swift-emit-silgen -Xllvm -sil-full-demangle -parse-as-library -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck %s + +var zero: Int = 0 + +func use(_: Int) {} + +func takeInt(_ a : Int) {} + +public struct DidSetWillSetTests { + // CHECK-LABEL: sil [ossa] @$s9observers010DidSetWillC5TestsV{{[_0-9a-zA-Z]*}}fC + public init(x : Int) { + // Accesses to didset/willset variables are direct in init methods and dtors. + a = x + a = x + + // CHECK: bb0(%0 : $Int, %1 : $@thin DidSetWillSetTests.Type): + // CHECK: [[SELF:%.*]] = mark_uninitialized [rootself] + // CHECK: [[PB_SELF:%.*]] = project_box [[SELF]] + // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PB_SELF]] + // CHECK: [[P1:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a + // CHECK-NEXT: assign %0 to [[P1]] + // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PB_SELF]] + // CHECK: [[P2:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a + // CHECK-NEXT: assign %0 to [[P2]] + } + + public var a: Int { + // CHECK-LABEL: sil private [ossa] @$s9observers010DidSetWillC5TestsV1a{{[_0-9a-zA-Z]*}}vw + willSet(newA) { + // CHECK: bb0(%0 : $Int, %1 : $*DidSetWillSetTests): + // CHECK-NEXT: debug_value %0 + // CHECK-NEXT: debug_value_addr %1 : $*DidSetWillSetTests + + takeInt(a) + + // CHECK: [[READ:%.*]] = begin_access [read] [unknown] %1 + // CHECK-NEXT: [[FIELDPTR:%.*]] = struct_element_addr [[READ]] : $*DidSetWillSetTests, #DidSetWillSetTests.a + // CHECK-NEXT: [[A:%.*]] = load [trivial] [[FIELDPTR]] : $*Int + // CHECK-NEXT: end_access [[READ]] + // CHECK: [[TAKEINTFN:%.*]] = function_ref @$s9observers7takeInt{{[_0-9a-zA-Z]*}}F + // CHECK-NEXT: apply [[TAKEINTFN]]([[A]]) : $@convention(thin) (Int) -> () + + takeInt(newA) + + // CHECK-NEXT: // function_ref observers.takeInt(Swift.Int) -> () + // CHECK-NEXT: [[TAKEINTFN:%.*]] = function_ref @$s9observers7takeInt{{[_0-9a-zA-Z]*}}F + // CHECK-NEXT: apply [[TAKEINTFN]](%0) : $@convention(thin) (Int) -> () + + a = zero // reassign, but don't infinite loop. + + // CHECK-NEXT: // function_ref observers.zero.unsafeMutableAddressor : Swift.Int + // CHECK-NEXT: [[ZEROFN:%.*]] = function_ref @$s9observers4zero{{[_0-9a-zA-Z]*}}vau + // CHECK-NEXT: [[ZERORAW:%.*]] = apply [[ZEROFN]]() : $@convention(thin) () -> Builtin.RawPointer + // CHECK-NEXT: [[ZEROADDR:%.*]] = pointer_to_address [[ZERORAW]] : $Builtin.RawPointer to [strict] $*Int + // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [dynamic] [[ZEROADDR]] : $*Int + // CHECK-NEXT: [[ZERO:%.*]] = load [trivial] [[READ]] + // CHECK-NEXT: end_access [[READ]] : $*Int + // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 + // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a + // CHECK-NEXT: assign [[ZERO]] to [[AADDR]] + } + + // CHECK-LABEL: sil private [ossa] @$s9observers010DidSetWillC5TestsV1a{{[_0-9a-zA-Z]*}}vW + didSet { + // CHECK: bb0(%0 : $Int, %1 : $*DidSetWillSetTests): + // CHECK-NEXT: debug + // CHECK-NEXT: debug_value_addr %1 : $*DidSetWillSetTests + + takeInt(a) + + // CHECK: [[READ:%.*]] = begin_access [read] [unknown] %1 + // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[READ]] : $*DidSetWillSetTests, #DidSetWillSetTests.a + // CHECK-NEXT: [[A:%.*]] = load [trivial] [[AADDR]] : $*Int + // CHECK-NEXT: end_access [[READ]] + // CHECK-NEXT: // function_ref observers.takeInt(Swift.Int) -> () + // CHECK-NEXT: [[TAKEINTFN:%.*]] = function_ref @$s9observers7takeInt{{[_0-9a-zA-Z]*}}F + // CHECK-NEXT: apply [[TAKEINTFN]]([[A]]) : $@convention(thin) (Int) -> () + + (self).a = zero // reassign, but don't infinite loop. + + // CHECK-NEXT: // function_ref observers.zero.unsafeMutableAddressor : Swift.Int + // CHECK-NEXT: [[ZEROFN:%.*]] = function_ref @$s9observers4zero{{[_0-9a-zA-Z]*}}vau + // CHECK-NEXT: [[ZERORAW:%.*]] = apply [[ZEROFN]]() : $@convention(thin) () -> Builtin.RawPointer + // CHECK-NEXT: [[ZEROADDR:%.*]] = pointer_to_address [[ZERORAW]] : $Builtin.RawPointer to [strict] $*Int + // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [dynamic] [[ZEROADDR]] : $*Int + // CHECK-NEXT: [[ZERO:%.*]] = load [trivial] [[READ]] + // CHECK-NEXT: end_access [[READ]] : $*Int + // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 + // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a + // CHECK-NEXT: assign [[ZERO]] to [[AADDR]] + } + } + + // This is the synthesized getter and setter for the willset/didset variable. + + // CHECK-LABEL: sil [transparent] [serialized] [ossa] @$s9observers010DidSetWillC5TestsV1aSivg + // CHECK: bb0(%0 : $DidSetWillSetTests): + // CHECK-NEXT: debug_value %0 + // CHECK-NEXT: %2 = struct_extract %0 : $DidSetWillSetTests, #DidSetWillSetTests.a + // CHECK-NEXT: return %2 : $Int{{.*}} // id: %3 + + + // CHECK-LABEL: sil [ossa] @$s9observers010DidSetWillC5TestsV1aSivs + // CHECK: bb0(%0 : $Int, %1 : $*DidSetWillSetTests): + // CHECK-NEXT: debug_value %0 + // CHECK-NEXT: debug_value_addr %1 + + // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] %1 + // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[READ]] : $*DidSetWillSetTests, #DidSetWillSetTests.a + // CHECK-NEXT: [[OLDVAL:%.*]] = load [trivial] [[AADDR]] : $*Int + // CHECK-NEXT: end_access [[READ]] + // CHECK-NEXT: debug_value [[OLDVAL]] : $Int, let, name "tmp" + + // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 + // CHECK-NEXT: // function_ref {{.*}}.DidSetWillSetTests.a.willset : Swift.Int + // CHECK-NEXT: [[WILLSETFN:%.*]] = function_ref @$s9observers010DidSetWillC5TestsV1a{{[_0-9a-zA-Z]*}}vw + // CHECK-NEXT: apply [[WILLSETFN]](%0, [[WRITE]]) : $@convention(method) (Int, @inout DidSetWillSetTests) -> () + // CHECK-NEXT: end_access [[WRITE]] + // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 + // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a + // CHECK-NEXT: assign %0 to [[AADDR]] : $*Int + // CHECK-NEXT: end_access [[WRITE]] + // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 + // CHECK-NEXT: // function_ref {{.*}}.DidSetWillSetTests.a.didset : Swift.Int + // CHECK-NEXT: [[DIDSETFN:%.*]] = function_ref @$s9observers010DidSetWillC5TestsV1a{{[_0-9a-zA-Z]*}}vW : $@convention(method) (Int, @inout DidSetWillSetTests) -> () + // CHECK-NEXT: apply [[DIDSETFN]]([[OLDVAL]], [[WRITE]]) : $@convention(method) (Int, @inout DidSetWillSetTests) -> () + + // CHECK-LABEL: sil hidden [ossa] @$s9observers010DidSetWillC5TestsV8testReadSiyF + // CHECK: [[SELF:%.*]] = begin_access [read] [unknown] %0 : $*DidSetWillSetTests + // CHECK-NEXT: [[PROP:%.*]] = struct_element_addr [[SELF]] : $*DidSetWillSetTests + // CHECK-NEXT: [[LOAD:%.*]] = load [trivial] [[PROP]] : $*Int + // CHECK-NEXT: end_access [[SELF]] : $*DidSetWillSetTests + // CHECK-NEXT: return [[LOAD]] : $Int + mutating func testRead() -> Int { + return a + } + + // CHECK-LABEL: sil hidden [ossa] @$s9observers010DidSetWillC5TestsV9testWrite5inputySi_tF + // CHECK: [[SELF:%.*]] = begin_access [modify] [unknown] %1 : $*DidSetWillSetTests + // CHECK-NEXT: // function_ref observers.DidSetWillSetTests.a.setter + // CHECK-NEXT: [[SETTER:%.*]] = function_ref @$s9observers010DidSetWillC5TestsV1aSivs + // CHECK-NEXT: apply [[SETTER]](%0, [[SELF]]) + // CHECK-NEXT: end_access [[SELF]] : $*DidSetWillSetTests + // CHECK-NEXT: [[RET:%.*]] = tuple () + // CHECK-NEXT: return [[RET]] : $() + mutating func testWrite(input: Int) { + a = input + } + + // CHECK-LABEL: sil hidden [ossa] @$s9observers010DidSetWillC5TestsV13testReadWrite5inputySi_tF + // CHECK: [[SELF:%.*]] = begin_access [modify] [unknown] %1 : $*DidSetWillSetTests + // CHECK-NEXT: [[TEMP:%.*]] = alloc_stack $Int + // CHECK-NEXT: [[PROP:%.*]] = struct_element_addr [[SELF]] : $*DidSetWillSetTests + // CHECK-NEXT: [[LOAD:%.*]] = load [trivial] [[PROP]] : $*Int + // CHECK-NEXT: store [[LOAD]] to [trivial] [[TEMP]] : $*Int + // (modification goes here) + // CHECK: [[RELOAD:%.*]] = load [trivial] [[TEMP]] : $*Int + // CHECK-NEXT: // function_ref observers.DidSetWillSetTests.a.setter + // CHECK-NEXT: [[SETTER:%.*]] = function_ref @$s9observers010DidSetWillC5TestsV1aSivs + // CHECK-NEXT: apply [[SETTER]]([[RELOAD]], [[SELF]]) + // CHECK-NEXT: end_access [[SELF]] : $*DidSetWillSetTests + // CHECK-NEXT: dealloc_stack [[TEMP]] : $*Int + // CHECK-NEXT: [[RET:%.*]] = tuple () + // CHECK-NEXT: return [[RET]] : $() + mutating func testReadWrite(input: Int) { + a += input + } +} + + +// Test global observing properties. + +var global_observing_property : Int = zero { + // The variable is initialized with "zero". + // CHECK-LABEL: sil private [ossa] @globalinit_{{.*}}_func1 : $@convention(c) () -> () { + // CHECK: bb0: + // CHECK-NEXT: alloc_global @$s9observers25global_observing_propertySiv + // CHECK-NEXT: %1 = global_addr @$s9observers25global_observing_propertySivp : $*Int + // CHECK: observers.zero.unsafeMutableAddressor + // CHECK: return + + // global_observing_property's setter needs to call didSet. + + // CHECK-LABEL: sil private [ossa] @$s9observers25global_observing_property{{[_0-9a-zA-Z]*}}vW + didSet { + // The didSet implementation needs to call takeInt. + takeInt(global_observing_property) + + // CHECK: function_ref observers.takeInt + // CHECK-NEXT: function_ref @$s9observers7takeInt{{[_0-9a-zA-Z]*}}F + + // Setting the variable from within its own didSet doesn't recursively call didSet. + global_observing_property = zero + + // CHECK: // function_ref observers.global_observing_property.unsafeMutableAddressor : Swift.Int + // CHECK-NEXT: [[ADDRESSOR:%.*]] = function_ref @$s9observers25global_observing_propertySivau : $@convention(thin) () -> Builtin.RawPointer + // CHECK-NEXT: [[ADDRESS:%.*]] = apply [[ADDRESSOR]]() : $@convention(thin) () -> Builtin.RawPointer + // CHECK-NEXT: [[POINTER:%.*]] = pointer_to_address [[ADDRESS]] : $Builtin.RawPointer to [strict] $*Int + // CHECK-NEXT: // function_ref observers.zero.unsafeMutableAddressor : Swift.Int + // CHECK-NEXT: [[ZEROFN:%.*]] = function_ref @$s9observers4zero{{[_0-9a-zA-Z]*}}vau + // CHECK-NEXT: [[ZERORAW:%.*]] = apply [[ZEROFN]]() : $@convention(thin) () -> Builtin.RawPointer + // CHECK-NEXT: [[ZEROADDR:%.*]] = pointer_to_address [[ZERORAW]] : $Builtin.RawPointer to [strict] $*Int + // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [dynamic] [[ZEROADDR]] : $*Int + // CHECK-NEXT: [[ZERO:%.*]] = load [trivial] [[READ]] + // CHECK-NEXT: end_access [[READ]] : $*Int + // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[POINTER]] : $*Int + // CHECK-NEXT: assign [[ZERO]] to [[WRITE]] : $*Int + // CHECK-NEXT: end_access [[WRITE]] : $*Int + // CHECK-NOT: function_ref @$s9observers25global_observing_property{{[_0-9a-zA-Z]*}}vW + // CHECK: end sil function + } + // CHECK-LABEL: sil hidden [ossa] @$s9observers25global_observing_property{{[_0-9a-zA-Z]*}}vs + // CHECK: function_ref observers.global_observing_property.unsafeMutableAddressor + // CHECK-NEXT: function_ref @$s9observers25global_observing_property{{[_0-9a-zA-Z]*}}vau + // CHECK: function_ref observers.global_observing_property.didset + // CHECK-NEXT: function_ref @$s9observers25global_observing_property{{[_0-9a-zA-Z]*}}vW + +} + +func force_global_observing_property_setter() { + let x = global_observing_property + global_observing_property = x +} + +// Test local observing properties. + +// CHECK-LABEL: sil hidden [ossa] @$s9observers24local_observing_property{{[_0-9a-zA-Z]*}}SiF +func local_observing_property(_ arg: Int) { + var localproperty: Int = arg { + didSet { + takeInt(localproperty) + localproperty = zero + } + } + + takeInt(localproperty) + localproperty = arg + + // Alloc and initialize the property to the argument value. + // CHECK: bb0([[ARG:%[0-9]+]] : $Int) + // CHECK: [[BOX:%[0-9]+]] = alloc_box ${ var Int } + // CHECK: [[PB:%.*]] = project_box [[BOX]] + // CHECK: store [[ARG]] to [trivial] [[PB]] +} + +// didSet of localproperty (above) +// Ensure that setting the variable from within its own didSet doesn't recursively call didSet. + +// CHECK-LABEL: sil private [ossa] @$s9observers24local_observing_property{{[_0-9a-zA-Z]*}}SiF13localproperty{{[_0-9a-zA-Z]*}}SivW +// CHECK: bb0(%0 : $Int, %1 : @guaranteed ${ var Int }) +// CHECK: [[POINTER:%.*]] = project_box %1 : ${ var Int }, 0 +// CHECK: // function_ref observers.zero.unsafeMutableAddressor : Swift.Int +// CHECK-NEXT: [[ZEROFN:%.*]] = function_ref @$s9observers4zero{{[_0-9a-zA-Z]*}}vau +// CHECK-NEXT: [[ZERORAW:%.*]] = apply [[ZEROFN]]() : $@convention(thin) () -> Builtin.RawPointer +// CHECK-NEXT: [[ZEROADDR:%.*]] = pointer_to_address [[ZERORAW]] : $Builtin.RawPointer to [strict] $*Int +// CHECK-NEXT: [[READ:%.*]] = begin_access [read] [dynamic] [[ZEROADDR]] : $*Int +// CHECK-NEXT: [[ZERO:%.*]] = load [trivial] [[READ]] +// CHECK-NEXT: end_access [[READ]] : $*Int + +// CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] [[POINTER]] : $*Int +// CHECK-NEXT: assign [[ZERO]] to [[WRITE]] : $*Int +// CHECK-NEXT: end_access [[WRITE]] : $*Int +// CHECK-NOT: function_ref @$s9observers24local_observing_property{{[_0-9a-zA-Z]*}}SiF13localproperty{{[_0-9a-zA-Z]*}}SivW +// CHECK: end sil function + +func local_generic_observing_property(_ arg: Int, _: T) { + var localproperty1: Int = arg { + didSet { + takeInt(localproperty1) + } + } + + takeInt(localproperty1) + localproperty1 = arg + + var localproperty2: Int = arg { + didSet { + _ = T.self + takeInt(localproperty2) + } + } + + takeInt(localproperty2) + localproperty2 = arg +} + + +// observing properties don't work in @objc classes +@objc +class ObservingPropertyInObjCClass { + var bounds: Int { + willSet {} + didSet {} + } + + init(b: Int) { bounds = b } +} + +// CHECK-LABEL: sil hidden [ossa] @$s9observers32propertyWithDidSetTakingOldValueyyF : $@convention(thin) () -> () { +func propertyWithDidSetTakingOldValue() { + var p : Int = zero { + didSet(oldValue) { + // access to oldValue + use(oldValue) + // and newValue. + use(p) + } + } + + p = zero +} + +// CHECK: // setter of p #1 : Swift.Int in observers.propertyWithDidSetTakingOldValue() +// CHECK-NEXT: sil {{.*}} [ossa] @$s9observers32propertyWithDidSetTakingOldValueyyF1pL_Sivs +// CHECK: bb0([[ARG1:%.*]] : $Int, [[ARG2:%.*]] : @guaranteed ${ var Int }): +// CHECK-NEXT: debug_value [[ARG1]] : $Int, let, name "newValue", argno 1 +// CHECK-NEXT: [[ARG2_PB:%.*]] = project_box [[ARG2]] +// CHECK-NEXT: debug_value_addr [[ARG2_PB]] : $*Int, var, name "p", argno 2 +// CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] [[ARG2_PB]] +// CHECK-NEXT: [[ARG2_PB_VAL:%.*]] = load [trivial] [[READ]] : $*Int +// CHECK-NEXT: end_access [[READ]] +// CHECK-NEXT: debug_value [[ARG2_PB_VAL]] : $Int +// CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] [[ARG2_PB]] +// CHECK-NEXT: assign [[ARG1]] to [[WRITE]] : $*Int +// CHECK-NEXT: end_access [[WRITE]] +// SEMANTIC ARC TODO: Another case where we need to put the mark_function_escape on a new projection after a copy. +// CHECK-NEXT: mark_function_escape [[ARG2_PB]] +// CHECK-NEXT: // function_ref +// CHECK-NEXT: [[FUNC:%.*]] = function_ref @$s9observers32propertyWithDidSetTakingOldValueyyF1pL_SivW : $@convention(thin) (Int, @guaranteed { var Int }) -> () +// CHECK-NEXT: %{{.*}} = apply [[FUNC]]([[ARG2_PB_VAL]], [[ARG2]]) : $@convention(thin) (Int, @guaranteed { var Int }) -> () +// CHECK-NEXT: %{{.*}} = tuple () +// CHECK-NEXT: return %{{.*}} : $() +// CHECK-NEXT:} // end sil function '$s9observers32propertyWithDidSetTakingOldValue{{[_0-9a-zA-Z]*}}' + + +// Observing properties don't work with ownership types +class Ref {} + +struct ObservingPropertiesWithOwnershipTypes { + unowned var alwaysPresent : Ref { + didSet { + } + } + + init(res: Ref) { + alwaysPresent = res + } +} + +// CHECK-LABEL: sil private [ossa] @$s9observers37ObservingPropertiesWithOwnershipTypesV13alwaysPresentAA3RefCvW : $@convention(method) (@guaranteed Ref, @inout ObservingPropertiesWithOwnershipTypes) -> () { + +struct ObservingPropertiesWithOwnershipTypesInferred { + unowned var alwaysPresent = Ref() { + didSet { + } + } + + weak var maybePresent = nil as Ref? { + willSet { + } + } +} + +// CHECK-LABEL: sil private [ossa] @$s9observers45ObservingPropertiesWithOwnershipTypesInferredV13alwaysPresentAA3RefCvW : $@convention(method) (@guaranteed Ref, @inout ObservingPropertiesWithOwnershipTypesInferred) -> () { +// CHECK-LABEL: sil private [ossa] @$s9observers45ObservingPropertiesWithOwnershipTypesInferredV12maybePresentAA3RefCSgvw : $@convention(method) (@guaranteed Optional, @inout ObservingPropertiesWithOwnershipTypesInferred) -> () { + +// Initializing constructor tries to initialize computed property overridden with willSet/didSet +class ObservedBase { + var printInfo: Ref! +} +class ObservedDerived : ObservedBase { + override init() {} + override var printInfo: Ref! { + didSet { } + } +} + +// CHECK-LABEL: sil private [ossa] @$s9observers15ObservedDerivedC9printInfoAA3RefCSgvW : $@convention(method) (@guaranteed Optional, @guaranteed ObservedDerived) -> () { + + +/// crash when overriding internal property with +/// public property + +public class BaseClassWithInternalProperty { + var x: () = () +} + +public class DerivedClassWithPublicProperty : BaseClassWithInternalProperty { + public override var x: () { + didSet {} + } +} + +// CHECK-LABEL: sil hidden [transparent] [ossa] @$s9observers29BaseClassWithInternalPropertyC1xytvg + +// CHECK-LABEL: sil [transparent] [serialized] [ossa] @$s9observers30DerivedClassWithPublicPropertyC1xytvg +// CHECK: bb0([[SELF:%.*]] : @guaranteed $DerivedClassWithPublicProperty): +// CHECK: [[SELF_COPY:%.*]] = copy_value [[SELF]] : $DerivedClassWithPublicProperty +// CHECK-NEXT: [[SUPER:%.*]] = upcast [[SELF_COPY]] : $DerivedClassWithPublicProperty to $BaseClassWithInternalProperty +// CHECK-NEXT: [[BORROWED_SUPER:%.*]] = begin_borrow [[SUPER]] +// CHECK-NEXT: [[DOWNCAST_BORROWED_SUPER:%.*]] = unchecked_ref_cast [[BORROWED_SUPER]] : $BaseClassWithInternalProperty to $DerivedClassWithPublicProperty +// CHECK-NEXT: [[METHOD:%.*]] = super_method [[DOWNCAST_BORROWED_SUPER]] : $DerivedClassWithPublicProperty, #BaseClassWithInternalProperty.x!getter.1 : (BaseClassWithInternalProperty) -> () -> (), $@convention(method) (@guaranteed BaseClassWithInternalProperty) -> () +// CHECK-NEXT: [[RESULT:%.*]] = apply [[METHOD]]([[BORROWED_SUPER]]) : $@convention(method) (@guaranteed BaseClassWithInternalProperty) -> () +// CHECK-NEXT: end_borrow [[BORROWED_SUPER]] +// CHECK-NEXT: destroy_value [[SUPER]] : $BaseClassWithInternalProperty +// CHECK: } // end sil function '$s9observers30DerivedClassWithPublicPropertyC1xytvg' + + +// Make sure we use the correct substitutions when referencing a property in a +// generic base class +public class GenericBase { + var storage: T? = nil +} + +public class ConcreteDerived : GenericBase { + override var storage: Int? { + didSet {} + } +} + +// CHECK-LABEL: sil private [ossa] @$s9observers15ConcreteDerivedC7storageSiSgvW : $@convention(method) (Optional, @guaranteed ConcreteDerived) -> () { + + +// Make sure we upcast properly when the overridden property is in an ancestor +// of our superclass +public class BaseObserved { + var x: Int = 0 +} + +public class MiddleObserved : BaseObserved {} + +public class DerivedObserved : MiddleObserved { + override var x: Int { + didSet {} + } +} + +// CHECK-LABEL: sil private [ossa] @$s9observers15DerivedObservedC1xSivW : $@convention(method) (Int, @guaranteed DerivedObserved) -> () { diff --git a/test/SILGen/properties.swift b/test/SILGen/properties.swift index 2adf3e4fbdf53..4dfcb4cdc30f4 100644 --- a/test/SILGen/properties.swift +++ b/test/SILGen/properties.swift @@ -1,5 +1,5 @@ -// RUN: %target-swift-emit-silgen -module-name properties -Xllvm -sil-full-demangle -parse-as-library -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck %s +// RUN: %target-swift-emit-silgen -Xllvm -sil-full-demangle -parse-as-library -disable-objc-attr-requires-foundation-module -enable-objc-interop %s | %FileCheck %s var zero: Int = 0 @@ -469,303 +469,6 @@ func static_set(_ x: Int) { StaticProperty.foo = x } -func takeInt(_ a : Int) {} - -protocol ForceAccessors { - var a: Int { get set } -} - -struct DidSetWillSetTests: ForceAccessors { - // CHECK-LABEL: sil hidden [ossa] @$s10properties010DidSetWillC5TestsV{{[_0-9a-zA-Z]*}}fC - init(x : Int) { - // Accesses to didset/willset variables are direct in init methods and dtors. - a = x - a = x - - // CHECK: bb0(%0 : $Int, %1 : $@thin DidSetWillSetTests.Type): - // CHECK: [[SELF:%.*]] = mark_uninitialized [rootself] - // CHECK: [[PB_SELF:%.*]] = project_box [[SELF]] - // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PB_SELF]] - // CHECK: [[P1:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a - // CHECK-NEXT: assign %0 to [[P1]] - // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] [[PB_SELF]] - // CHECK: [[P2:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a - // CHECK-NEXT: assign %0 to [[P2]] - } - - var a: Int { - // CHECK-LABEL: sil private [ossa] @$s10properties010DidSetWillC5TestsV1a{{[_0-9a-zA-Z]*}}vw - willSet(newA) { - // CHECK: bb0(%0 : $Int, %1 : $*DidSetWillSetTests): - // CHECK-NEXT: debug_value %0 - // CHECK-NEXT: debug_value_addr %1 : $*DidSetWillSetTests - - takeInt(a) - - // CHECK: [[READ:%.*]] = begin_access [read] [unknown] %1 - // CHECK-NEXT: [[FIELDPTR:%.*]] = struct_element_addr [[READ]] : $*DidSetWillSetTests, #DidSetWillSetTests.a - // CHECK-NEXT: [[A:%.*]] = load [trivial] [[FIELDPTR]] : $*Int - // CHECK-NEXT: end_access [[READ]] - // CHECK: [[TAKEINTFN:%.*]] = function_ref @$s10properties7takeInt{{[_0-9a-zA-Z]*}}F - // CHECK-NEXT: apply [[TAKEINTFN]]([[A]]) : $@convention(thin) (Int) -> () - - takeInt(newA) - - // CHECK-NEXT: // function_ref properties.takeInt(Swift.Int) -> () - // CHECK-NEXT: [[TAKEINTFN:%.*]] = function_ref @$s10properties7takeInt{{[_0-9a-zA-Z]*}}F - // CHECK-NEXT: apply [[TAKEINTFN]](%0) : $@convention(thin) (Int) -> () - - a = zero // reassign, but don't infinite loop. - - // CHECK-NEXT: // function_ref properties.zero.unsafeMutableAddressor : Swift.Int - // CHECK-NEXT: [[ZEROFN:%.*]] = function_ref @$s10properties4zero{{[_0-9a-zA-Z]*}}vau - // CHECK-NEXT: [[ZERORAW:%.*]] = apply [[ZEROFN]]() : $@convention(thin) () -> Builtin.RawPointer - // CHECK-NEXT: [[ZEROADDR:%.*]] = pointer_to_address [[ZERORAW]] : $Builtin.RawPointer to [strict] $*Int - // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [dynamic] [[ZEROADDR]] : $*Int - // CHECK-NEXT: [[ZERO:%.*]] = load [trivial] [[READ]] - // CHECK-NEXT: end_access [[READ]] : $*Int - // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 - // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a - // CHECK-NEXT: assign [[ZERO]] to [[AADDR]] - } - - // CHECK-LABEL: sil private [ossa] @$s10properties010DidSetWillC5TestsV1a{{[_0-9a-zA-Z]*}}vW - didSet { - // CHECK: bb0(%0 : $Int, %1 : $*DidSetWillSetTests): - // CHECK-NEXT: debug - // CHECK-NEXT: debug_value_addr %1 : $*DidSetWillSetTests - - takeInt(a) - - // CHECK: [[READ:%.*]] = begin_access [read] [unknown] %1 - // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[READ]] : $*DidSetWillSetTests, #DidSetWillSetTests.a - // CHECK-NEXT: [[A:%.*]] = load [trivial] [[AADDR]] : $*Int - // CHECK-NEXT: end_access [[READ]] - // CHECK-NEXT: // function_ref properties.takeInt(Swift.Int) -> () - // CHECK-NEXT: [[TAKEINTFN:%.*]] = function_ref @$s10properties7takeInt{{[_0-9a-zA-Z]*}}F - // CHECK-NEXT: apply [[TAKEINTFN]]([[A]]) : $@convention(thin) (Int) -> () - - (self).a = zero // reassign, but don't infinite loop. - - // CHECK-NEXT: // function_ref properties.zero.unsafeMutableAddressor : Swift.Int - // CHECK-NEXT: [[ZEROFN:%.*]] = function_ref @$s10properties4zero{{[_0-9a-zA-Z]*}}vau - // CHECK-NEXT: [[ZERORAW:%.*]] = apply [[ZEROFN]]() : $@convention(thin) () -> Builtin.RawPointer - // CHECK-NEXT: [[ZEROADDR:%.*]] = pointer_to_address [[ZERORAW]] : $Builtin.RawPointer to [strict] $*Int - // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [dynamic] [[ZEROADDR]] : $*Int - // CHECK-NEXT: [[ZERO:%.*]] = load [trivial] [[READ]] - // CHECK-NEXT: end_access [[READ]] : $*Int - // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 - // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a - // CHECK-NEXT: assign [[ZERO]] to [[AADDR]] - } - } - - // This is the synthesized getter and setter for the willset/didset variable. - - // CHECK-LABEL: sil hidden [transparent] [ossa] @$s10properties010DidSetWillC5TestsV1aSivg - // CHECK: bb0(%0 : $DidSetWillSetTests): - // CHECK-NEXT: debug_value %0 - // CHECK-NEXT: %2 = struct_extract %0 : $DidSetWillSetTests, #DidSetWillSetTests.a - // CHECK-NEXT: return %2 : $Int{{.*}} // id: %3 - - - // CHECK-LABEL: sil hidden [ossa] @$s10properties010DidSetWillC5TestsV1aSivs - // CHECK: bb0(%0 : $Int, %1 : $*DidSetWillSetTests): - // CHECK-NEXT: debug_value %0 - // CHECK-NEXT: debug_value_addr %1 - - // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] %1 - // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[READ]] : $*DidSetWillSetTests, #DidSetWillSetTests.a - // CHECK-NEXT: [[OLDVAL:%.*]] = load [trivial] [[AADDR]] : $*Int - // CHECK-NEXT: end_access [[READ]] - // CHECK-NEXT: debug_value [[OLDVAL]] : $Int, let, name "tmp" - - // CHECK: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 - // CHECK-NEXT: // function_ref {{.*}}.DidSetWillSetTests.a.willset : Swift.Int - // CHECK-NEXT: [[WILLSETFN:%.*]] = function_ref @$s10properties010DidSetWillC5TestsV1a{{[_0-9a-zA-Z]*}}vw - // CHECK-NEXT: apply [[WILLSETFN]](%0, [[WRITE]]) : $@convention(method) (Int, @inout DidSetWillSetTests) -> () - // CHECK-NEXT: end_access [[WRITE]] - // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 - // CHECK-NEXT: [[AADDR:%.*]] = struct_element_addr [[WRITE]] : $*DidSetWillSetTests, #DidSetWillSetTests.a - // CHECK-NEXT: assign %0 to [[AADDR]] : $*Int - // CHECK-NEXT: end_access [[WRITE]] - // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] %1 - // CHECK-NEXT: // function_ref {{.*}}.DidSetWillSetTests.a.didset : Swift.Int - // CHECK-NEXT: [[DIDSETFN:%.*]] = function_ref @$s10properties010DidSetWillC5TestsV1a{{[_0-9a-zA-Z]*}}vW : $@convention(method) (Int, @inout DidSetWillSetTests) -> () - // CHECK-NEXT: apply [[DIDSETFN]]([[OLDVAL]], [[WRITE]]) : $@convention(method) (Int, @inout DidSetWillSetTests) -> () - - // CHECK-LABEL: sil hidden [ossa] @$s10properties010DidSetWillC5TestsV8testReadSiyF - // CHECK: [[SELF:%.*]] = begin_access [read] [unknown] %0 : $*DidSetWillSetTests - // CHECK-NEXT: [[PROP:%.*]] = struct_element_addr [[SELF]] : $*DidSetWillSetTests - // CHECK-NEXT: [[LOAD:%.*]] = load [trivial] [[PROP]] : $*Int - // CHECK-NEXT: end_access [[SELF]] : $*DidSetWillSetTests - // CHECK-NEXT: return [[LOAD]] : $Int - mutating func testRead() -> Int { - return a - } - - // CHECK-LABEL: sil hidden [ossa] @$s10properties010DidSetWillC5TestsV9testWrite5inputySi_tF - // CHECK: [[SELF:%.*]] = begin_access [modify] [unknown] %1 : $*DidSetWillSetTests - // CHECK-NEXT: // function_ref properties.DidSetWillSetTests.a.setter - // CHECK-NEXT: [[SETTER:%.*]] = function_ref @$s10properties010DidSetWillC5TestsV1aSivs - // CHECK-NEXT: apply [[SETTER]](%0, [[SELF]]) - // CHECK-NEXT: end_access [[SELF]] : $*DidSetWillSetTests - // CHECK-NEXT: [[RET:%.*]] = tuple () - // CHECK-NEXT: return [[RET]] : $() - mutating func testWrite(input: Int) { - a = input - } - - // CHECK-LABEL: sil hidden [ossa] @$s10properties010DidSetWillC5TestsV13testReadWrite5inputySi_tF - // CHECK: [[SELF:%.*]] = begin_access [modify] [unknown] %1 : $*DidSetWillSetTests - // CHECK-NEXT: [[TEMP:%.*]] = alloc_stack $Int - // CHECK-NEXT: [[PROP:%.*]] = struct_element_addr [[SELF]] : $*DidSetWillSetTests - // CHECK-NEXT: [[LOAD:%.*]] = load [trivial] [[PROP]] : $*Int - // CHECK-NEXT: store [[LOAD]] to [trivial] [[TEMP]] : $*Int - // (modification goes here) - // CHECK: [[RELOAD:%.*]] = load [trivial] [[TEMP]] : $*Int - // CHECK-NEXT: // function_ref properties.DidSetWillSetTests.a.setter - // CHECK-NEXT: [[SETTER:%.*]] = function_ref @$s10properties010DidSetWillC5TestsV1aSivs - // CHECK-NEXT: apply [[SETTER]]([[RELOAD]], [[SELF]]) - // CHECK-NEXT: end_access [[SELF]] : $*DidSetWillSetTests - // CHECK-NEXT: dealloc_stack [[TEMP]] : $*Int - // CHECK-NEXT: [[RET:%.*]] = tuple () - // CHECK-NEXT: return [[RET]] : $() - mutating func testReadWrite(input: Int) { - a += input - } -} - - -// Test global observing properties. - -var global_observing_property : Int = zero { - // The variable is initialized with "zero". - // CHECK-LABEL: sil private [ossa] @globalinit_{{.*}}_func1 : $@convention(c) () -> () { - // CHECK: bb0: - // CHECK-NEXT: alloc_global @$s10properties25global_observing_propertySiv - // CHECK-NEXT: %1 = global_addr @$s10properties25global_observing_propertySivp : $*Int - // CHECK: properties.zero.unsafeMutableAddressor - // CHECK: return - - // global_observing_property's setter needs to call didSet. - - // CHECK-LABEL: sil private [ossa] @$s10properties25global_observing_property{{[_0-9a-zA-Z]*}}vW - didSet { - // The didSet implementation needs to call takeInt. - takeInt(global_observing_property) - - // CHECK: function_ref properties.takeInt - // CHECK-NEXT: function_ref @$s10properties7takeInt{{[_0-9a-zA-Z]*}}F - - // Setting the variable from within its own didSet doesn't recursively call didSet. - global_observing_property = zero - - // CHECK: // function_ref properties.global_observing_property.unsafeMutableAddressor : Swift.Int - // CHECK-NEXT: [[ADDRESSOR:%.*]] = function_ref @$s10properties25global_observing_propertySivau : $@convention(thin) () -> Builtin.RawPointer - // CHECK-NEXT: [[ADDRESS:%.*]] = apply [[ADDRESSOR]]() : $@convention(thin) () -> Builtin.RawPointer - // CHECK-NEXT: [[POINTER:%.*]] = pointer_to_address [[ADDRESS]] : $Builtin.RawPointer to [strict] $*Int - // CHECK-NEXT: // function_ref properties.zero.unsafeMutableAddressor : Swift.Int - // CHECK-NEXT: [[ZEROFN:%.*]] = function_ref @$s10properties4zero{{[_0-9a-zA-Z]*}}vau - // CHECK-NEXT: [[ZERORAW:%.*]] = apply [[ZEROFN]]() : $@convention(thin) () -> Builtin.RawPointer - // CHECK-NEXT: [[ZEROADDR:%.*]] = pointer_to_address [[ZERORAW]] : $Builtin.RawPointer to [strict] $*Int - // CHECK-NEXT: [[READ:%.*]] = begin_access [read] [dynamic] [[ZEROADDR]] : $*Int - // CHECK-NEXT: [[ZERO:%.*]] = load [trivial] [[READ]] - // CHECK-NEXT: end_access [[READ]] : $*Int - // CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [dynamic] [[POINTER]] : $*Int - // CHECK-NEXT: assign [[ZERO]] to [[WRITE]] : $*Int - // CHECK-NEXT: end_access [[WRITE]] : $*Int - // CHECK-NOT: function_ref @$s10properties25global_observing_property{{[_0-9a-zA-Z]*}}vW - // CHECK: end sil function - } - // CHECK-LABEL: sil hidden [ossa] @$s10properties25global_observing_property{{[_0-9a-zA-Z]*}}vs - // CHECK: function_ref properties.global_observing_property.unsafeMutableAddressor - // CHECK-NEXT: function_ref @$s10properties25global_observing_property{{[_0-9a-zA-Z]*}}vau - // CHECK: function_ref properties.global_observing_property.didset - // CHECK-NEXT: function_ref @$s10properties25global_observing_property{{[_0-9a-zA-Z]*}}vW - -} - -func force_global_observing_property_setter() { - let x = global_observing_property - global_observing_property = x -} - -// Test local observing properties. - -// CHECK-LABEL: sil hidden [ossa] @$s10properties24local_observing_property{{[_0-9a-zA-Z]*}}SiF -func local_observing_property(_ arg: Int) { - var localproperty: Int = arg { - didSet { - takeInt(localproperty) - localproperty = zero - } - } - - takeInt(localproperty) - localproperty = arg - - // Alloc and initialize the property to the argument value. - // CHECK: bb0([[ARG:%[0-9]+]] : $Int) - // CHECK: [[BOX:%[0-9]+]] = alloc_box ${ var Int } - // CHECK: [[PB:%.*]] = project_box [[BOX]] - // CHECK: store [[ARG]] to [trivial] [[PB]] -} - -// didSet of localproperty (above) -// Ensure that setting the variable from within its own didSet doesn't recursively call didSet. - -// CHECK-LABEL: sil private [ossa] @$s10properties24local_observing_property{{[_0-9a-zA-Z]*}}SiF13localproperty{{[_0-9a-zA-Z]*}}SivW -// CHECK: bb0(%0 : $Int, %1 : @guaranteed ${ var Int }) -// CHECK: [[POINTER:%.*]] = project_box %1 : ${ var Int }, 0 -// CHECK: // function_ref properties.zero.unsafeMutableAddressor : Swift.Int -// CHECK-NEXT: [[ZEROFN:%.*]] = function_ref @$s10properties4zero{{[_0-9a-zA-Z]*}}vau -// CHECK-NEXT: [[ZERORAW:%.*]] = apply [[ZEROFN]]() : $@convention(thin) () -> Builtin.RawPointer -// CHECK-NEXT: [[ZEROADDR:%.*]] = pointer_to_address [[ZERORAW]] : $Builtin.RawPointer to [strict] $*Int -// CHECK-NEXT: [[READ:%.*]] = begin_access [read] [dynamic] [[ZEROADDR]] : $*Int -// CHECK-NEXT: [[ZERO:%.*]] = load [trivial] [[READ]] -// CHECK-NEXT: end_access [[READ]] : $*Int - -// CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] [[POINTER]] : $*Int -// CHECK-NEXT: assign [[ZERO]] to [[WRITE]] : $*Int -// CHECK-NEXT: end_access [[WRITE]] : $*Int -// CHECK-NOT: function_ref @$s10properties24local_observing_property{{[_0-9a-zA-Z]*}}SiF13localproperty{{[_0-9a-zA-Z]*}}SivW -// CHECK: end sil function - -func local_generic_observing_property(_ arg: Int, _: T) { - var localproperty1: Int = arg { - didSet { - takeInt(localproperty1) - } - } - - takeInt(localproperty1) - localproperty1 = arg - - var localproperty2: Int = arg { - didSet { - _ = T.self - takeInt(localproperty2) - } - } - - takeInt(localproperty2) - localproperty2 = arg -} - - -// observing properties don't work in @objc classes -@objc -class ObservingPropertyInObjCClass { - var bounds: Int { - willSet {} - didSet {} - } - - init(b: Int) { bounds = b } -} - - // Superclass init methods should not get direct access to be class properties. // rdar://16151899 @@ -797,43 +500,6 @@ class rdar16151899Derived : rdar16151899Base { } } - -func propertyWithDidSetTakingOldValue() { - var p : Int = zero { - didSet(oldValue) { - // access to oldValue - use(oldValue) - // and newValue. - use(p) - } - } - - p = zero -} - -// CHECK: // setter of p #1 : Swift.Int in properties.propertyWithDidSetTakingOldValue() -// CHECK-NEXT: sil {{.*}} [ossa] @$s10properties32propertyWithDidSetTakingOldValueyyF1pL_Sivs -// CHECK: bb0([[ARG1:%.*]] : $Int, [[ARG2:%.*]] : @guaranteed ${ var Int }): -// CHECK-NEXT: debug_value [[ARG1]] : $Int, let, name "newValue", argno 1 -// CHECK-NEXT: [[ARG2_PB:%.*]] = project_box [[ARG2]] -// CHECK-NEXT: debug_value_addr [[ARG2_PB]] : $*Int, var, name "p", argno 2 -// CHECK-NEXT: [[READ:%.*]] = begin_access [read] [unknown] [[ARG2_PB]] -// CHECK-NEXT: [[ARG2_PB_VAL:%.*]] = load [trivial] [[READ]] : $*Int -// CHECK-NEXT: end_access [[READ]] -// CHECK-NEXT: debug_value [[ARG2_PB_VAL]] : $Int -// CHECK-NEXT: [[WRITE:%.*]] = begin_access [modify] [unknown] [[ARG2_PB]] -// CHECK-NEXT: assign [[ARG1]] to [[WRITE]] : $*Int -// CHECK-NEXT: end_access [[WRITE]] -// SEMANTIC ARC TODO: Another case where we need to put the mark_function_escape on a new projection after a copy. -// CHECK-NEXT: mark_function_escape [[ARG2_PB]] -// CHECK-NEXT: // function_ref -// CHECK-NEXT: [[FUNC:%.*]] = function_ref @$s10properties32propertyWithDidSetTakingOldValueyyF1pL_SivW : $@convention(thin) (Int, @guaranteed { var Int }) -> () -// CHECK-NEXT: %{{.*}} = apply [[FUNC]]([[ARG2_PB_VAL]], [[ARG2]]) : $@convention(thin) (Int, @guaranteed { var Int }) -> () -// CHECK-NEXT: %{{.*}} = tuple () -// CHECK-NEXT: return %{{.*}} : $() -// CHECK-NEXT:} // end sil function '$s10properties32propertyWithDidSetTakingOldValue{{[_0-9a-zA-Z]*}}' - - class BaseProperty { var x : Int { get {} set {} } } @@ -879,30 +545,6 @@ struct ReferenceStorageTypeRValues { } -// Observing properties don't work with ownership types -struct ObservingPropertiesWithOwnershipTypes { - unowned var alwaysPresent : Ref { - didSet { - } - } - - init(res: Ref) { - alwaysPresent = res - } -} - -struct ObservingPropertiesWithOwnershipTypesInferred { - unowned var alwaysPresent = Ref(i: 0) { - didSet { - } - } - - weak var maybePresent = nil as Ref? { - willSet { - } - } -} - // property accessor synthesization of weak variables doesn't work protocol WeakPropertyProtocol { var maybePresent : Ref? { get set } @@ -930,19 +572,6 @@ func getX(_ g: SomeGenericStruct) -> Int { } -// Initializing constructor tries to initialize computed property overridden with willSet/didSet -class ObservedBase { - var printInfo: Ref! -} -class ObservedDerived : ObservedBase { - override init() {} - override var printInfo: Ref! { - didSet { } - } -} - - - /// Class properties should be allowed in protocols, even without stored class properties protocol ProtoWithClassProp { static var x: Int { get } @@ -1166,33 +795,6 @@ struct CrashWithUnnamedSubscript : ProtocolWithReadWriteSubscript { } -/// crash when overriding internal property with -/// public property - -public class BaseClassWithInternalProperty { - var x: () = () -} - -public class DerivedClassWithPublicProperty : BaseClassWithInternalProperty { - public override var x: () { - didSet {} - } -} - -// CHECK-LABEL: sil hidden [transparent] [ossa] @$s10properties29BaseClassWithInternalPropertyC1xytvg - -// CHECK-LABEL: sil [transparent] [serialized] [ossa] @$s10properties30DerivedClassWithPublicPropertyC1xytvg -// CHECK: bb0([[SELF:%.*]] : @guaranteed $DerivedClassWithPublicProperty): -// CHECK: [[SELF_COPY:%.*]] = copy_value [[SELF]] : $DerivedClassWithPublicProperty -// CHECK-NEXT: [[SUPER:%.*]] = upcast [[SELF_COPY]] : $DerivedClassWithPublicProperty to $BaseClassWithInternalProperty -// CHECK-NEXT: [[BORROWED_SUPER:%.*]] = begin_borrow [[SUPER]] -// CHECK-NEXT: [[DOWNCAST_BORROWED_SUPER:%.*]] = unchecked_ref_cast [[BORROWED_SUPER]] : $BaseClassWithInternalProperty to $DerivedClassWithPublicProperty -// CHECK-NEXT: [[METHOD:%.*]] = super_method [[DOWNCAST_BORROWED_SUPER]] : $DerivedClassWithPublicProperty, #BaseClassWithInternalProperty.x!getter.1 : (BaseClassWithInternalProperty) -> () -> (), $@convention(method) (@guaranteed BaseClassWithInternalProperty) -> () -// CHECK-NEXT: [[RESULT:%.*]] = apply [[METHOD]]([[BORROWED_SUPER]]) : $@convention(method) (@guaranteed BaseClassWithInternalProperty) -> () -// CHECK-NEXT: end_borrow [[BORROWED_SUPER]] -// CHECK-NEXT: destroy_value [[SUPER]] : $BaseClassWithInternalProperty -// CHECK: } // end sil function '$s10properties30DerivedClassWithPublicPropertyC1xytvg' - // Make sure that we can handle this AST: // (load_expr // (open_existential_expr diff --git a/test/SILGen/stored_property_default_arg.swift b/test/SILGen/stored_property_default_arg.swift index 97414122b67a4..43e2ec7590537 100644 --- a/test/SILGen/stored_property_default_arg.swift +++ b/test/SILGen/stored_property_default_arg.swift @@ -76,15 +76,13 @@ struct J { // CHECK-LABEL: sil hidden [ossa] @$s27stored_property_default_arg16checkOptionalNilyyF : $@convention(thin) () -> () { func checkOptionalNil() { -// CHECK: {{.*}} = metatype $@thin Optional.Type -// CHECK-NEXT: [[L1_REF:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK: [[L1_REF:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK-NEXT: function_ref J.init(k:l:) // CHECK-NEXT: [[J1_REF:%.*]] = function_ref @$s27stored_property_default_arg1JV1k1lACSbSg_SiSgtcfC : $@convention(method) (Optional, Optional, @thin J.Type) -> J // CHECK-NEXT: {{.*}} = apply [[J1_REF]]({{.*}}, [[L1_REF]], {{.*}}) : $@convention(method) (Optional, Optional, @thin J.Type) -> J let m = J(k: true) -// CHECK: {{.*}} = metatype $@thin Optional.Type -// CHECK-NEXT: [[K1_REF:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK: [[K1_REF:%.*]] = enum $Optional, #Optional.none!enumelt // CHECK: function_ref J.init(k:l:) // CHECK-NEXT: [[J2_REF:%.*]] = function_ref @$s27stored_property_default_arg1JV1k1lACSbSg_SiSgtcfC : $@convention(method) (Optional, Optional, @thin J.Type) -> J // CHECK-NEXT: {{.*}} = apply [[J2_REF]]([[K1_REF]], {{.*}}, {{.*}}) : $@convention(method) (Optional, Optional, @thin J.Type) -> J