New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SR-2814] Swift does not correctly pass in multiple optional strings to C function #45418
Comments
Some problem with the optional-string-to-pointer conversion? |
It looks like we just throw away the buffer owner instead of keeping it alive until we're done with the string. Yikes.
|
I think attempting to fix this will be a good learning opportunity for me. If someone wants to fix it quickly, or if I don't submit a pull request by Jan 22, they should feel free to snatch this task away from me. The problem can be reduced further, by using a function that takes only two parameters instead of four: // MyCFunction.h
#include <stdio.h>
void multiString(const char *arg0, const char *arg1) {
printf("%p: %c %c %c\n", arg0, arg0[0], arg0[1], arg0[2]);
printf("%p: %c %c %c\n", arg1, arg1[0], arg1[1], arg1[2]);
} // MySwift.swift
let input0: String? = "Zero"
let input1: String? = "One"
multiString(input0, input1) These files, compiled with 0x7fc62c40b7a0: O n e
0x7fc62c40b7a0: O n e (It's important to note, however, that it's not 100% guaranteed the above incorrect output will be produced. I noticed that around 1 in 10 invocations actually produced correct output. I guess this isn't entirely deterministic right now.) I emitted SIL using swiftc -sdk `xcrun --show-sdk-path` MySwift.sil -import-objc-header MyCFunction.h -emit-sil
Assertion failed: ((Qualifier != StoreOwnershipQualifier::Unqualified) || F.hasUnqualifiedOwnership() && "Unqualified inst in qualified function"), function createStore, file /Users/bgesiak/GitHub/apple/swift/include/swift/SIL/SILBuilder.h, line 500.
Stack dump:
0. Program arguments: /Users/bgesiak/GitHub/apple/build/Ninja-ReleaseAssert+swift-DebugAssert/swift-macosx-x86_64/bin/swift -frontend -emit-sil -primary-file MySwift-mangled.sil -target x86_64-apple-macosx10.9 -enable-objc-interop -sdk /Applications/Xcode_8.1_fb.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -import-objc-header MyCFunction.h -color-diagnostics -module-name main -o -
1. With parser at source location: MySwift-mangled.sil:35:3
[1] 55375 abort -sdk `xcrun --show-sdk-path` MySwift-mangled.sil -import-objc-header Not being able to modify the SIL directly, I tried modifying the lib/SIL source code. The following change "fixes" the problem: diff --git a/lib/SIL/TypeLowering.cpp b/lib/SIL/TypeLowering.cpp
index 3c7379e..bde9ead 100644
--- a/lib/SIL/TypeLowering.cpp
+++ b/lib/SIL/TypeLowering.cpp
@@ -872,7 +872,7 @@ namespace {
B.createDestroyValue(loc, value);
return;
}
- B.emitReleaseValueAndFold(loc, value);
+ // B.emitReleaseValueAndFold(loc, value);
}
void emitLoweredDestroyValue(SILBuilder &B, SILLocation loc, SILValue value, After having made the above modification, 0x7fe8f3d0bbc0: Z e r
0x7fe8f3d0b610: O n e The SIL was identical, except the calls to Of course, the "fix" above probably leaks memory like crazy. Next, I'll read the source code to learn about the reasoning behind emitting |
Some updates: I noticed that The I find it helpful to read through the backtrace, reversed, to see what's going on and how the Swift compiler gets from main(argc_=12, argv_=...) + 4811 at driver.cpp:160
swift::performFrontend(Args=ArrayRef<const char *> @ ..., Argv0="/Users/bgesiak/GitHub/apple/build/Ninja-ReleaseAssert+swift-DebugAssert/swift-macosx-x86_64/bin/swift", MainAddr=..., observer=...) + 13279 at FrontendTool.cpp:969
performCompile(Instance=..., Invocation=..., Args=ArrayRef<const char *> @ ..., ReturnValue=..., observer=...) + 8926 at FrontendTool.cpp:523
swift::performSILGeneration(sf=..., options=..., startElem=Optional<unsigned int> @ ..., makeModuleFragile=false) + 135 at SILGen.cpp:1509
swift::SILModule::constructSIL(mod=..., options=..., SF=..., startElem=Optional<unsigned int> @ ..., makeModuleFragile=false, isWholeModule=false) + 1037 at SILGen.cpp:1445
swift::Lowering::SILGenModule::emitSourceFile(this=..., sf=..., startElem=0) + 263 at SILGen.cpp:1409
swift::ASTVisitor<swift::Lowering::SILGenModule, void, void, void, void, void, void>::visit(this=..., D=...) + 188 at DeclNodes.def:93
swift::Lowering::SILGenModule::visitTopLevelCodeDecl(this=..., td=...) + 668 at SILGen.cpp:1219
swift::Lowering::SILGenFunction::emitIgnoredExpr(this=..., E=...) + 887 at SILGenExpr.cpp:3622
swift::Lowering::SILGenFunction::emitRValue(this=..., E=..., C=SGFContext @ ...) + 201 at SILGenExpr.cpp:3572
swift::ASTVisitor<RValueEmitter, swift::Lowering::RValue, void, void, void, void, void, swift::Lowering::SGFContext>::visit(this=..., E=..., AA=SGFContext @ ...) + 3549 at ExprNodes.def:112
swift::ASTVisitor<RValueEmitter, swift::Lowering::RValue, void, void, void, void, void, swift::Lowering::SGFContext>::visitCallExpr(this=..., E=..., AA=SGFContext @ ...) + 74 at ExprNodes.def:112
RValueEmitter::visitApplyExpr(this=..., E=..., C=SGFContext @ ...) + 66 at SILGenExpr.cpp:334
swift::Lowering::SILGenFunction::emitApplyExpr(this=..., e=..., c=SGFContext @ ...) + 132 at SILGenApply.cpp:4597
CallEmission::apply(this=..., C=SGFContext @ ...) + 6953 at SILGenApply.cpp:4376
CallSite::emit(this=..., gen=..., origParamType=AbstractionPattern @ ..., lowering=..., args=..., inoutArgs=..., foreignError=..., foreignSelf=...)::ParamLowering&, llvm::SmallVectorImpl<swift::Lowering::ManagedValue>&, llvm::SmallVectorImpl<std::__1::pair<swift::Lowering::LValue, swift::SILLocation> >&, llvm::Optional<swift::ForeignErrorConvention> const&, swift::ImportAsMemberStatus const&) && + 666 at SILGenApply.cpp:4063
ArgEmitter::emitTopLevel(this=..., arg=..., origParamType=AbstractionPattern @ ...) + 128 at SILGenApply.cpp:2866
ArgEmitter::emit(this=..., arg=..., origParamType=AbstractionPattern @ ...) + 179 at SILGenApply.cpp:2876
ArgEmitter::emitExpanded(this=..., arg=..., origParamType=AbstractionPattern @ ...) + 1935 at SILGenApply.cpp:3016
ArgEmitter::emit(this=..., arg=..., origParamType=AbstractionPattern @ ...) + 1779 at SILGenApply.cpp:2951
ArgEmitter::emitDirect(this=..., arg=..., loweredSubstArgType=SILType @ ..., origParamType=AbstractionPattern @ ..., param=SILParameterInfo @ ...) + 650 at SILGenApply.cpp:3141
swift::Lowering::ArgumentSource::getAsSingleValue(this=..., gen=..., C=SGFContext @ ...) && + 631 at ArgumentSource.cpp:117
swift::Lowering::SILGenFunction::emitRValueAsSingleValue(this=..., E=..., C=SGFContext @ ...) + 82 at SILGenExpr.cpp:3628
swift::Lowering::SILGenFunction::emitRValue(this=..., E=..., C=SGFContext @ ...) + 201 at SILGenExpr.cpp:3572
swift::ASTVisitor<RValueEmitter, swift::Lowering::RValue, void, void, void, void, void, swift::Lowering::SGFContext>::visit(this=..., E=..., AA=SGFContext @ ...) + 3249 at ExprNodes.def:107
RValueEmitter::visitOptionalEvaluationExpr(this=..., E=..., C=SGFContext @ ...) + 1948 at SILGenExpr.cpp:3073
swift::Lowering::Scope::pop(this=...) + 128 at Scope.h:58
swift::Lowering::Scope::popImpl(this=...) + 303 at Scope.h:41
swift::Lowering::CleanupManager::endScope(this=..., depth=(Depth = 0), l=CleanupLocation @ ...) + 222 at Cleanup.cpp:94
swift::Lowering::CleanupManager::emitCleanups(this=..., depth=(Depth = 0), l=CleanupLocation @ ..., popCleanups=true) + 452 at Cleanup.cpp:75
ReleaseValueCleanup::emit(this=..., gen=..., l=CleanupLocation @ ...) + 266 at SILGenDecl.cpp:229
swift::SILBuilder::emitDestroyValueOperation(this=..., Loc=SILLocation @ ..., v=SILValue @ ...) + 267 at SILBuilder.h:1586
LoadableEnumTypeLowering::emitDestroyValue(this=..., B=..., loc=SILLocation @ ..., value=SILValue @ ...) const + 16 at TypeLowering.cpp:871 Unfortunately, since I'm not experienced with SIL in general, I'm not sure what part of this stacktrace is "incorrect." Is it the fact that As a next step, I plan on looking at what happens when I'm on a bit of a wild goose chase at this point, so any hints would be helpful! Otherwise I'll keep plugging away. |
Interesting investigation so far. I don't know much about SIL/Gen/Lowering et al. either, but here are some thoughts from looking at what you've written and reproducing it myself:
|
My feeling is that the problem here is that temporaries that are not forwarded should live until the end of an apply. I think this probably has to do with argument emission. That being said, I am going to be making changes in this area, so I would suggest either waiting or at least give me a heads up with what you are planning. Another thing, emtiReleaseValueAndFold is no longer used by SILGen today. If it is, then it is a bug. |
I don't have a ton of time to work on this anymore. Still, it was a fun way to learn more about SILGen, so thank you! |
Has this been fixed in the meantime? I cannot reproduce the problem anymore with Xcode 13.4.1/Swift 5.6.1. The output of the test program is
as one would expect. |
Environment
Tested on macOS 10.12 (16A323) with Swift 3.0 running on Xcode 8.0 (8A218a).
Additional Detail from JIRA
md5: cb58a1980dcaff3969ab4708cb4038fa
relates to:
Issue Description:
In a C function taking in multiple strings (a.k.a. `const char *`s), passing in optional strings in Swift (a.k.a. `String?`) causes the last passed in string to appear for all arguments.
Consider the following C function:
Calling it from Swift with the following input:
Results in an output like:
In the correct output, the pointers would all be different and the characters would correspond to the correct input strings. Passing in non-optional strings results in the correct behaviour, as does a C function with a single string type.
The text was updated successfully, but these errors were encountered: