Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/swift/SIL/SILFunctionConventions.h
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,9 @@ class SILFunctionConventions {
- getNumIndirectSILErrorResults()];
}

/// WARNING: Do not use this from SILGen!
/// Use methods such as `isSILIndirect` or query the ParameterInfo instead.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the comment explain why not? :)

Copy link
Member Author

@kavon kavon Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little hard to explain, but it's because SILFunctionConventions::isSILIndirect goes through the SILModuleConventions::isIndirectSILParam query, which correctly answers whether an argument is indirect. Similarly it will do that for SILResultInfo for indirect returns.

In the future, I'd like to revise SILFunctionConventions::getSILArgumentConvention , or possibly eliminate it. It was recently refactored in 65e2e8c and I don't think it's going in the right direction, as it's trying to think about results as if they are arguments, when the SILFunctionConventions use a type distinction between SILParameterInfo and SILResultInfo to help avoid confusion there.

///
/// Return the SIL argument convention of apply/entry argument at
/// the given argument index.
SILArgumentConvention getSILArgumentConvention(unsigned index) const;
Expand Down
2 changes: 2 additions & 0 deletions lib/SIL/IR/SILArgument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ SILParameterInfo SILFunctionArgument::getKnownParameterInfo() const {
return getFunction()->getConventions().getParamInfoForSILArg(getIndex());
}

/// WARNING: Do not use this from SILGen!
/// Use methods such as `isSILIndirect` or query the ParameterInfo instead.
SILArgumentConvention
SILFunctionConventions::getSILArgumentConvention(unsigned index) const {
assert(index < getNumSILArguments());
Expand Down
4 changes: 2 additions & 2 deletions lib/SIL/Verifier/SILVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3189,9 +3189,9 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
CanSILFunctionType initTy = initFn->getType().castTo<SILFunctionType>();
SILFunctionConventions initConv(initTy, AI->getModule());

require(initConv.getNumIndirectSILResults() ==
require(initConv.getResults().size() ==
AI->getNumInitializedProperties(),
"init function has invalid number of indirect results");
"init function has invalid number of results");
checkAssigOrInitInstAccessorArgs(Src->getType(), initConv);
}

Expand Down
92 changes: 56 additions & 36 deletions lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1822,35 +1822,46 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor(
RegularLocation Loc(value);
Loc.markAutoGenerated();

auto loweredFuncDeclTy = F.getLoweredFunctionType();
bool isLocalContext = vd->getDeclContext()->isLocalContext();

auto createArgument = [&](VarDecl *property, SILType type,
bool markUninitialized = false) {
auto *arg = ParamDecl::createImplicit(
getASTContext(), property->getBaseIdentifier(),
property->getBaseIdentifier(), property->getInterfaceType(),
declContext, ParamSpecifier::InOut);

RegularLocation loc(property);
loc.markAutoGenerated();

SILValue argValue = F.begin()->createFunctionArgument(type, arg);
if (markUninitialized) {
argValue = B.createMarkUninitializedOut(loc, argValue);
}
auto backingStorage = vd->getPropertyWrapperBackingProperty();
Type returnTy = vd->getPropertyWrapperBackingPropertyType();

/// This thunk uses its own return convention, unlike a usual function,
/// by always returning its result indirect, even if the type is trivial.
///
/// Because of that, much of the work that would normally be handled in SILGen
/// using standard function conventions is done manually here, when
/// -enable-sil-opaque-values is disabled. Part of the reason for this is
/// that calls to this accessor are lowered after DefiniteInitialization.
if (useLoweredAddresses()) {
returnTy = TupleType::getEmpty(F.getASTContext());

auto loweredFuncDeclTy = F.getLoweredFunctionType();
auto createArgument = [&](VarDecl *property, SILType type,
bool markUninitialized = false) {
auto *arg = ParamDecl::createImplicit(
getASTContext(), property->getBaseIdentifier(),
property->getBaseIdentifier(), property->getInterfaceType(),
declContext, ParamSpecifier::InOut);

RegularLocation loc(property);
loc.markAutoGenerated();

SILValue argValue = F.begin()->createFunctionArgument(type, arg);
if (markUninitialized) {
argValue = B.createMarkUninitializedOut(loc, argValue);
}

VarLocs[arg] = VarLoc(argValue, SILAccessEnforcement::Static);
InitAccessorArgumentMappings[property] = arg;
};
VarLocs[arg] = VarLoc(argValue, SILAccessEnforcement::Static);
InitAccessorArgumentMappings[property] = arg;
};

// Emit @out backing storage argument
auto backingStorage = vd->getPropertyWrapperBackingProperty();
auto backingStorageTy = getSILTypeInContext(
loweredFuncDeclTy->getResults()[0], loweredFuncDeclTy);
createArgument(backingStorage, backingStorageTy, /*markUninitialized=*/true);
// Emit @out backing storage argument
auto backingStorageTy = getSILTypeInContext(
loweredFuncDeclTy->getResults()[0], loweredFuncDeclTy);
createArgument(backingStorage, backingStorageTy, /*markUninitialized=*/true);
}

// Emit `newValue` argument
// Create the `newValue` argument
ParameterList *params = nullptr;
auto newValueParam = new (ctx)
ParamDecl(SourceLoc(), SourceLoc(), ctx.getIdentifier("$input_value"),
Expand All @@ -1864,9 +1875,10 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor(

// Nominal contexts have an extra trailing metatype argument,
// local contexts do not
const bool isLocalContext = vd->getDeclContext()->isLocalContext();
auto numIgnoredParams = isLocalContext ? 0 : 1;
emitBasicProlog(declContext, params,
/*selfParam=*/nullptr, TupleType::getEmpty(F.getASTContext()),
/*selfParam=*/nullptr, returnTy,
/*errorType=*/std::nullopt,
/*throwsLoc=*/SourceLoc(),
/*ignored parameters*/ numIgnoredParams);
Expand All @@ -1875,9 +1887,14 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor(
if (!isLocalContext)
emitConstructorMetatypeArg(*this, vd);

prepareEpilog(declContext, TupleType::getEmpty(F.getASTContext()),
prepareEpilog(declContext, returnTy,
std::nullopt, CleanupLocation(Loc));

if (EmitProfilerIncrement)
emitProfilerIncrement(value);

FullExpr scope(Cleanups, CleanupLocation(value));

// Create an opaque value binding that maps 'newValue' to the wrapper's
// wrappedValue AST placeholder. This makes the argument available when
// init(wrappedValue:) is emitted
Expand All @@ -1889,16 +1906,19 @@ void SILGenFunction::emitPropertyWrappedFieldInitAccessor(
maybeEmitValueOfLocalVarDecl(newValueParam, AccessKind::Read));
assert(value == initInfo.getInitFromWrappedValue());

// Prepare InitializationPtr for the @out return buffer
FullExpr scope(Cleanups, CleanupLocation(value));
auto backingStorageArg = InitAccessorArgumentMappings[backingStorage];
auto backingStorageAddr = VarLocs[backingStorageArg].value;
InitializationPtr init(new KnownAddressInitialization(backingStorageAddr));
if (useLoweredAddresses()) {
// Prepare InitializationPtr for the @out return buffer
auto backingStorageArg = InitAccessorArgumentMappings[backingStorage];
auto backingStorageAddr = VarLocs[backingStorageArg].value;
InitializationPtr init(new KnownAddressInitialization(backingStorageAddr));

// Intialize the @out buffer with the given expression
emitExprInto(value, init.get());
// Intialize the @out buffer with the given expression
emitExprInto(value, init.get());
} else {
emitReturnExpr(Loc, value);
}

// Emit epilog/cleanups
emitEpilog(Loc);
mergeCleanupBlocks();
}
}
9 changes: 5 additions & 4 deletions lib/SILGen/SILGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1883,9 +1883,10 @@ SILGenFunction::emitApplyOfSetterToBase(SILLocation loc, SILDeclRef setter,
assert(base);

SILValue capturedBase;
unsigned argIdx = setterConv.getNumSILArguments() - 1;
unsigned argIdx = setterConv.getSILArgIndexOfSelf();
auto paramInfo = setterConv.getParamInfoForSILArg(argIdx);

if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) {
if (paramInfo.isIndirectMutating()) {
capturedBase = base.getValue();
} else if (base.getType().isAddress() &&
base.getType().getObjectType() ==
Expand Down Expand Up @@ -1975,9 +1976,9 @@ void SILGenFunction::emitAssignOrInit(SILLocation loc, ManagedValue selfValue,
SILFunctionConventions initConv(initTy, SGM.M);

auto newValueArgIdx = initConv.getSILArgIndexOfFirstParam();
auto newValueParamInfo = initConv.getParamInfoForSILArg(newValueArgIdx);
// If we need the argument in memory, materialize an address.
if (initConv.getSILArgumentConvention(newValueArgIdx)
.isIndirectConvention() &&
if (initConv.isSILIndirect(newValueParamInfo) &&
!newValue.getType().isAddress()) {
newValue = newValue.materialize(*this, loc);
}
Expand Down
18 changes: 9 additions & 9 deletions lib/SILGen/SILGenLValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1528,8 +1528,10 @@ namespace {
return cast<AccessorDecl>(Accessor.getFuncDecl());
}

ManagedValue emitValue(SILGenFunction &SGF, SILLocation loc, VarDecl *field,
ArgumentSource &&value, AccessorKind accessorKind) {
ManagedValue emitValueForAssignOrInit(SILGenFunction &SGF, SILLocation loc,
VarDecl *field,
ArgumentSource &&value,
AccessorKind accessorKind) {
auto accessorInfo = SGF.getConstantInfo(
SGF.getTypeExpansionContext(),
SILDeclRef(field->getOpaqueAccessor(accessorKind)));
Expand All @@ -1553,7 +1555,8 @@ namespace {
assert(value.isRValue());
ManagedValue Mval =
std::move(value).asKnownRValue(SGF).getAsSingleValue(SGF, loc);
auto param = accessorTy->getParameters()[0];
auto paramIdx = accessorConv.getSILArgIndexOfFirstParam();
auto param = accessorConv.getParamInfoForSILArg(paramIdx);
SILType loweredSubstArgType = Mval.getType();
if (param.isIndirectInOut()) {
loweredSubstArgType =
Expand All @@ -1572,11 +1575,8 @@ namespace {
fieldTy->getCanonicalType());
}

auto newValueArgIdx = accessorConv.getNumIndirectSILResults();
// If we need the argument in memory, materialize an address.
if (accessorConv.getSILArgumentConvention(newValueArgIdx)
.isIndirectConvention() &&
!Mval.getType().isAddress()) {
if (accessorConv.isSILIndirect(param) && !Mval.getType().isAddress()) {
Mval = Mval.materialize(SGF, loc);
}

Expand Down Expand Up @@ -1610,7 +1610,7 @@ namespace {
override {
VarDecl *field = cast<VarDecl>(Storage);
auto Mval =
emitValue(SGF, loc, field, std::move(value), AccessorKind::Init);
emitValueForAssignOrInit(SGF, loc, field, std::move(value), AccessorKind::Init);
SGF.emitAssignOrInit(loc, base, field, Mval, Substitutions);
}

Expand Down Expand Up @@ -1875,7 +1875,7 @@ namespace {

// Create the assign_or_init SIL instruction
auto Mval =
emitValue(SGF, loc, field, std::move(value), AccessorKind::Set);
emitValueForAssignOrInit(SGF, loc, field, std::move(value), AccessorKind::Set);
auto selfOrLocal = selfMetatype ? base.getValue() : proj.forward(SGF);
SGF.B.createAssignOrInit(loc, field, selfOrLocal, Mval.forward(SGF),
initFn.getValue(), setterFn,
Expand Down
12 changes: 6 additions & 6 deletions lib/SILGen/SILGenProlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1331,19 +1331,18 @@ static void emitCaptureArguments(SILGenFunction &SGF,
case CaptureKind::Immutable: {
auto argIndex = SGF.F.begin()->getNumArguments();
// Non-escaping stored decls are captured as the address of the value.
auto argConv = SGF.F.getConventions().getSILArgumentConvention(argIndex);
bool isInOut = (argConv == SILArgumentConvention::Indirect_Inout ||
argConv == SILArgumentConvention::Indirect_InoutAliasable);
auto param = SGF.F.getConventions().getParamInfoForSILArg(argIndex);
if (SGF.F.getConventions().isSILIndirect(param)) {
auto fnConv = SGF.F.getConventions();
auto paramInfo = fnConv.getParamInfoForSILArg(argIndex);
if (fnConv.isSILIndirect(paramInfo)) {
ty = ty.getAddressType();
}
auto *fArg = SGF.F.begin()->createFunctionArgument(ty, VD);
fArg->setClosureCapture(true);
arg = SILValue(fArg);

if (isNoImplicitCopy && !arg->getType().isMoveOnly()) {
switch (argConv) {
// FIXME: this incompatible with -enable-sil-opaque-values
switch (fnConv.getSILArgumentConvention(argIndex)) {
case SILArgumentConvention::Indirect_Inout:
case SILArgumentConvention::Indirect_InoutAliasable:
case SILArgumentConvention::Indirect_In:
Expand Down Expand Up @@ -1377,6 +1376,7 @@ static void emitCaptureArguments(SILGenFunction &SGF,
// in SIL since it is illegal to capture an inout value in an escaping
// closure. The later code knows how to handle that we have the
// mark_unresolved_non_copyable_value here.
bool isInOut = paramInfo.isIndirectMutating();
if (isInOut && arg->getType().isMoveOnly()) {
arg = SGF.B.createMarkUnresolvedNonCopyableValueInst(
Loc, arg,
Expand Down
Loading