@@ -738,35 +738,26 @@ void SILGenFunction::emitReturnExpr(SILLocation branchLoc,
738738 }
739739 } else if (F.getConventions ().hasGuaranteedResult () ||
740740 F.getConventions ().hasGuaranteedAddressResult ()) {
741- // If the return expression is a literal, emit as a regular return
742- // expression/
741+ auto *afd = cast<AbstractFunctionDecl>(FunctionDC->getAsDecl ());
743742 if (isa<LiteralExpr>(ret)) {
743+ // If the return expression is a literal, emit as a regular return
744+ // expression.
744745 auto RV = emitRValue (ret);
745746 std::move (RV).forwardAll (*this , directResults);
746747 } else {
747- // If the return expression is not a projection, diagnose as error.
748748 FormalEvaluationScope scope (*this );
749749 auto storageRefResult =
750750 StorageRefResult::findStorageReferenceExprForBorrow (ret);
751751 auto lvExpr = storageRefResult.getTransitiveRoot ();
752+ // If the return expression is not an lvalue, diagnose.
752753 if (!lvExpr) {
753754 diagnose (getASTContext (), ret->getStartLoc (),
754755 diag::invalid_borrow_accessor_return);
755756 diagnose (getASTContext (), ret->getStartLoc (),
756757 diag::borrow_accessor_not_a_projection_note);
757758 return ;
758759 }
759- // If the return expression is not a projection of self, diagnose as
760- // error.
761- auto *baseExpr = lookThroughProjections (storageRefResult.getStorageRef ());
762- if (!baseExpr->isSelfExprOf (
763- cast<AbstractFunctionDecl>(FunctionDC->getAsDecl ()))) {
764- diagnose (getASTContext (), ret->getStartLoc (),
765- diag::invalid_borrow_accessor_return);
766- diagnose (getASTContext (), ret->getStartLoc (),
767- diag::borrow_accessor_not_a_projection_note);
768- return ;
769- }
760+
770761 // Emit return value at +0.
771762 LValueOptions options;
772763 auto lvalue = emitLValue (ret,
@@ -776,34 +767,71 @@ void SILGenFunction::emitReturnExpr(SILLocation branchLoc,
776767 F.getConventions ().hasGuaranteedResult ()
777768 ? options.forGuaranteedReturn (true )
778769 : options.forGuaranteedAddressReturn (true ));
779- auto result =
780- tryEmitProjectedLValue (ret, std::move (lvalue), TSanKind::None);
781- if (!result) {
782- diagnose (getASTContext (), ret->getStartLoc (),
783- diag::invalid_borrow_accessor_return);
784- diagnose (getASTContext (), ret->getStartLoc (),
785- diag::borrow_accessor_not_a_projection_note);
786- return ;
787- }
788- // For now diagnose multiple return statements in borrow/mutate accessors.
789- // We need additional support for this.
790- // 1. Address phis are banned in SIL.
791- // 2. borrowed from is not inserted in SILGenCleanup.
792- if (!ReturnDest.getBlock ()->getPredecessorBlocks ().empty ()) {
793- diagnose (getASTContext (), ret->getStartLoc (),
794- diag::invalid_multiple_return_borrow_accessor);
795- return ;
796- }
797770
798- auto resultValue = result->getValue ();
799- SILType selfType = F.getSelfArgument ()->getType ();
800- if (selfType.isMoveOnly () && F.getConventions ().hasGuaranteedResult ()) {
801- // If we are returning the result of borrow accessor, strip the
802- // unnecessary copy_value + mark_unresolved_non_copyable_value
803- // instructions.
804- resultValue = lookThroughMoveOnlyCheckerPattern (resultValue);
771+ if (afd->getAttrs ().hasAttribute <UnsafeSelfDependentResultAttr>()) {
772+ // If the accessor is annotated with @_unsafeSelfDependentResultAttr,
773+ // disable diagnosing the return expression.
774+ // This is needed to implement borrow accessors for Unsafe*Pointer based
775+ // Container types where the compiler cannot analyze the safety of
776+ // return expressions based on pointer arithmetic and unsafe addressors.
777+ // Example:
778+ // public struct Container<Element: ~Copyable>: ~Copyable {
779+ // var _storage: UnsafeMutableBufferPointer<Element>
780+ // var _count: Int
781+ //
782+ // public subscript(index: Int) -> Element {
783+ // @_unsafeSelfDependentResult
784+ // borrow {
785+ // precondition(index >= 0 && index < _count, "Index out of
786+ // bounds") return
787+ // _storage.baseAddress.unsafelyUnwrapped.advanced(by:
788+ // index).pointee
789+ // }
790+ // }
791+ // }
792+ auto resultValue = emitBorrowedLValue (ret, std::move (lvalue));
793+ directResults.push_back (resultValue.getValue ());
794+ } else {
795+ // If the return expression is not a transitive projection of self,
796+ // diagnose.
797+ auto *baseExpr =
798+ lookThroughProjections (storageRefResult.getStorageRef ());
799+ if (!baseExpr->isSelfExprOf (afd)) {
800+ diagnose (getASTContext (), ret->getStartLoc (),
801+ diag::invalid_borrow_accessor_return);
802+ diagnose (getASTContext (), ret->getStartLoc (),
803+ diag::borrow_accessor_not_a_projection_note);
804+ return ;
805+ }
806+ auto result =
807+ tryEmitProjectedLValue (ret, std::move (lvalue), TSanKind::None);
808+ if (!result) {
809+ diagnose (getASTContext (), ret->getStartLoc (),
810+ diag::invalid_borrow_accessor_return);
811+ diagnose (getASTContext (), ret->getStartLoc (),
812+ diag::borrow_accessor_not_a_projection_note);
813+ return ;
814+ }
815+ // For now diagnose multiple return statements in borrow/mutate
816+ // accessors. We need additional support for this.
817+ // 1. Address phis are banned in SIL.
818+ // 2. borrowed from is not inserted in SILGenCleanup.
819+ if (!ReturnDest.getBlock ()->getPredecessorBlocks ().empty ()) {
820+ diagnose (getASTContext (), ret->getStartLoc (),
821+ diag::invalid_multiple_return_borrow_accessor);
822+ return ;
823+ }
824+
825+ auto resultValue = result->getValue ();
826+ SILType selfType = F.getSelfArgument ()->getType ();
827+ if (selfType.isMoveOnly () && F.getConventions ().hasGuaranteedResult ()) {
828+ // If we are returning the result of borrow accessor, strip the
829+ // unnecessary copy_value + mark_unresolved_non_copyable_value
830+ // instructions.
831+ resultValue = lookThroughMoveOnlyCheckerPattern (resultValue);
832+ }
833+ directResults.push_back (resultValue);
805834 }
806- directResults.push_back (resultValue);
807835 }
808836 } else {
809837 // SILValue return.
0 commit comments