Skip to content

Commit a728305

Browse files
authored
Merge pull request #18647 from slavapestov/inout-in-parens-is-still-inout
Easy InOutType removals and bug fix
2 parents de58075 + f316d46 commit a728305

File tree

8 files changed

+80
-128
lines changed

8 files changed

+80
-128
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,16 +1064,6 @@ ERROR(c_function_pointer_from_function_with_context,none,
10641064
// MARK: Type Check Declarations
10651065
//------------------------------------------------------------------------------
10661066

1067-
ERROR(var_type_not_materializable,none,
1068-
"variable has type %0 which includes nested inout parameters",
1069-
(Type))
1070-
ERROR(param_type_non_materializable_tuple,none,
1071-
"named parameter has type %0 which includes nested inout parameters",
1072-
(Type))
1073-
ERROR(enum_element_not_materializable,none,
1074-
"enum case has type %0 which includes nested inout parameters",
1075-
(Type))
1076-
10771067
ERROR(missing_initializer_def,PointsToFirstBadToken,
10781068
"initializer requires a body", ())
10791069

lib/SILGen/SILGenProlog.cpp

Lines changed: 25 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,19 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
8585
SILGenFunction &SGF;
8686
SILBasicBlock *parent;
8787
SILLocation loc;
88-
bool functionArgs;
8988
ArrayRef<SILParameterInfo> &parameters;
9089

91-
EmitBBArguments(SILGenFunction &sgf, SILBasicBlock *parent,
92-
SILLocation l, bool functionArgs,
90+
EmitBBArguments(SILGenFunction &sgf, SILBasicBlock *parent, SILLocation l,
9391
ArrayRef<SILParameterInfo> &parameters)
94-
: SGF(sgf), parent(parent), loc(l), functionArgs(functionArgs),
95-
parameters(parameters) {}
92+
: SGF(sgf), parent(parent), loc(l), parameters(parameters) {}
9693

9794
ManagedValue visitType(CanType t) {
98-
auto argType = SGF.getLoweredType(t->getInOutObjectType());
99-
if (t->is<InOutType>())
95+
return visitType(t, /*isInOut=*/false);
96+
}
97+
98+
ManagedValue visitType(CanType t, bool isInOut) {
99+
auto argType = SGF.getLoweredType(t);
100+
if (isInOut)
100101
argType = SILType::getPrimitiveAddressType(argType.getASTType());
101102

102103
// Pop the next parameter info.
@@ -112,16 +113,18 @@ class EmitBBArguments : public CanTypeVisitor<EmitBBArguments,
112113
ManagedValue mv = SGF.B.createInputFunctionArgument(
113114
argType, loc.getAsASTNode<ValueDecl>());
114115

116+
if (isInOut)
117+
return mv;
118+
115119
// If the value is a (possibly optional) ObjC block passed into the entry
116120
// point of the function, then copy it so we can treat the value reliably
117121
// as a heap object. Escape analysis can eliminate this copy if it's
118122
// unneeded during optimization.
119123
CanType objectType = t;
120124
if (auto theObjTy = t.getOptionalObjectType())
121125
objectType = theObjTy;
122-
if (functionArgs
123-
&& isa<FunctionType>(objectType)
124-
&& cast<FunctionType>(objectType)->getRepresentation()
126+
if (isa<FunctionType>(objectType) &&
127+
cast<FunctionType>(objectType)->getRepresentation()
125128
== FunctionType::Representation::Block) {
126129
SILValue blockCopy = SGF.B.createCopyBlock(loc, mv.getValue());
127130
mv = SGF.emitManagedRValueWithCleanup(blockCopy);
@@ -208,13 +211,18 @@ struct ArgumentInitHelper {
208211

209212
unsigned getNumArgs() const { return ArgNo; }
210213

211-
ManagedValue makeArgument(Type ty, SILBasicBlock *parent, SILLocation l) {
214+
ManagedValue makeArgument(Type ty, bool isInOut, SILBasicBlock *parent,
215+
SILLocation l) {
212216
assert(ty && "no type?!");
213217

214218
// Create an RValue by emitting destructured arguments into a basic block.
215219
CanType canTy = ty->eraseDynamicSelfType()->getCanonicalType();
216-
return EmitBBArguments(SGF, parent, l, /*functionArgs*/ true,
217-
parameters).visit(canTy);
220+
EmitBBArguments argEmitter(SGF, parent, l, parameters);
221+
222+
// Note: inouts of tuples are not exploded, so we bypass visit().
223+
if (isInOut)
224+
return argEmitter.visitType(canTy, /*isInOut=*/true);
225+
return argEmitter.visit(canTy);
218226
}
219227

220228
/// Create a SILArgument and store its value into the given Initialization,
@@ -223,23 +231,9 @@ struct ArgumentInitHelper {
223231
SILLocation loc(vd);
224232
loc.markAsPrologue();
225233

226-
ManagedValue argrv = makeArgument(ty, parent, loc);
234+
ManagedValue argrv = makeArgument(ty, vd->isInOut(), parent, loc);
227235

228-
// Create a shadow copy of inout parameters so they can be captured
229-
// by closures. The InOutDeshadowing guaranteed optimization will
230-
// eliminate the variable if it is not needed.
231236
if (vd->isInOut()) {
232-
SILValue address = argrv.getUnmanagedValue();
233-
234-
// As a special case, don't introduce a local variable for
235-
// Builtin.UnsafeValueBuffer, which is not copyable.
236-
if (isa<BuiltinUnsafeValueBufferType>(ty->getCanonicalType())) {
237-
// FIXME: mark a debug location?
238-
SGF.VarLocs[vd] = SILGenFunction::VarLoc::get(address);
239-
SGF.B.createDebugValueAddr(loc, address,
240-
SILDebugVariable(vd->isLet(), ArgNo));
241-
return;
242-
}
243237
assert(argrv.getType().isAddress() && "expected inout to be address");
244238
} else if (auto *metatypeTy = ty->getAs<MetatypeType>()) {
245239
// This is a hack to deal with the fact that Self.Type comes in as a
@@ -269,8 +263,8 @@ struct ArgumentInitHelper {
269263

270264
void emitParam(ParamDecl *PD) {
271265
auto type = PD->getType();
272-
if (PD->isInOut())
273-
type = InOutType::get(type); // FIXME remove InOutType
266+
267+
assert(type->isMaterializable());
274268

275269
++ArgNo;
276270
if (PD->hasName()) {
@@ -282,26 +276,11 @@ struct ArgumentInitHelper {
282276
}
283277

284278
void emitAnonymousParam(Type type, SILLocation paramLoc, ParamDecl *PD) {
285-
// Allow non-materializable tuples to be bound to anonymous parameters.
286-
//
287-
// FIXME: Remove this once -swift-version 3 code generation goes away.
288-
if (!type->isMaterializable()) {
289-
if (auto tupleType = type->getAs<TupleType>()) {
290-
for (auto eltType : tupleType->getElementTypes()) {
291-
emitAnonymousParam(eltType, paramLoc, nullptr);
292-
}
293-
return;
294-
}
295-
}
296-
297279
// A value bound to _ is unused and can be immediately released.
298280
Scope discardScope(SGF.Cleanups, CleanupLocation(PD));
299281

300282
// Manage the parameter.
301-
ManagedValue argrv = makeArgument(type, &*f.begin(), paramLoc);
302-
303-
// Don't do anything else if we don't have a parameter.
304-
if (!PD) return;
283+
auto argrv = makeArgument(type, PD->isInOut(), &*f.begin(), paramLoc);
305284

306285
// Emit debug information for the argument.
307286
SILLocation loc(PD);

lib/Sema/TypeCheckConstraints.cpp

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2419,27 +2419,7 @@ bool TypeChecker::typeCheckBinding(Pattern *&pattern, Expr *&initializer,
24192419

24202420
auto resultTy = typeCheckExpression(initializer, DC, contextualType,
24212421
contextualPurpose, flags, &listener);
2422-
2423-
// If we detected that the initializer would have a non-materializable type,
2424-
// complain.
2425-
if (resultTy && listener.isInOut()) {
2426-
diagnose(pattern->getStartLoc(), diag::var_type_not_materializable,
2427-
resultTy);
2428-
2429-
pattern->setType(ErrorType::get(Context));
2430-
pattern->forEachVariable([&](VarDecl *var) {
2431-
// Don't change the type of a variable that we've been able to
2432-
// compute a type for.
2433-
if (var->hasType() &&
2434-
!var->getType()->hasUnboundGenericType() &&
2435-
!var->getType()->hasError())
2436-
return;
2437-
2438-
var->markInvalid();
2439-
});
2440-
2441-
return true;
2442-
}
2422+
assert(!listener.isInOut());
24432423

24442424
if (resultTy) {
24452425
TypeResolutionOptions options = TypeResolutionFlags::OverrideType;

lib/Sema/TypeCheckDecl.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4402,14 +4402,9 @@ void TypeChecker::validateDecl(ValueDecl *D) {
44024402

44034403
EED->setSignatureIsValidated();
44044404

4405-
// Require the carried type to be materializable.
44064405
if (auto argTy = EED->getArgumentInterfaceType()) {
4407-
assert(!argTy->hasLValueType() && "enum element cannot carry @lvalue");
4408-
4409-
if (!argTy->isMaterializable()) {
4410-
diagnose(EED->getLoc(), diag::enum_element_not_materializable, argTy);
4411-
EED->setInterfaceType(ErrorType::get(Context));
4412-
}
4406+
assert(argTy->isMaterializable());
4407+
(void) argTy;
44134408
}
44144409

44154410
break;

lib/Sema/TypeCheckPattern.cpp

Lines changed: 19 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -789,19 +789,6 @@ static bool validateParameterType(ParamDecl *decl, DeclContext *DC,
789789
TL.setType(Ty);
790790
}
791791

792-
// If the user did not explicitly write 'let', 'var', or 'inout', we'll let
793-
// type inference figure out what went wrong in detail.
794-
if (decl->getSpecifierLoc().isValid()) {
795-
// If the param is not a 'let' and it is not an 'inout'.
796-
// It must be a 'var'. Provide helpful diagnostics like a shadow copy
797-
// in the function body to fix the 'var' attribute.
798-
if (!decl->isImmutable() && !decl->isImplicit() &&
799-
(Ty.isNull() || !Ty->is<InOutType>()) && !hadError) {
800-
decl->setInvalid();
801-
hadError = true;
802-
}
803-
}
804-
805792
if (hadError)
806793
TL.setInvalidType(TC.Context);
807794

@@ -861,18 +848,26 @@ bool TypeChecker::typeCheckParameterList(ParameterList *PL, DeclContext *DC,
861848
param->markInvalid();
862849
hadError = true;
863850
} else {
864-
if (type->is<InOutType>())
865-
param->setSpecifier(VarDecl::Specifier::InOut);
866-
param->setInterfaceType(type->getInOutObjectType());
851+
param->setInterfaceType(type);
867852
}
868853

869854
checkTypeModifyingDeclAttributes(param);
870855
if (!hadError) {
871-
if (isa<InOutTypeRepr>(typeRepr)) {
856+
auto *nestedRepr = typeRepr;
857+
858+
// Look through parens here; other than parens, specifiers
859+
// must appear at the top level of a parameter type.
860+
while (auto *tupleRepr = dyn_cast<TupleTypeRepr>(nestedRepr)) {
861+
if (!tupleRepr->isParenType())
862+
break;
863+
nestedRepr = tupleRepr->getElementType(0);
864+
}
865+
866+
if (isa<InOutTypeRepr>(nestedRepr)) {
872867
param->setSpecifier(VarDecl::Specifier::InOut);
873-
} else if (isa<SharedTypeRepr>(typeRepr)) {
868+
} else if (isa<SharedTypeRepr>(nestedRepr)) {
874869
param->setSpecifier(VarDecl::Specifier::Shared);
875-
} else if (isa<OwnedTypeRepr>(typeRepr)) {
870+
} else if (isa<OwnedTypeRepr>(nestedRepr)) {
876871
param->setSpecifier(VarDecl::Specifier::Owned);
877872
}
878873
}
@@ -1626,29 +1621,22 @@ bool TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE,
16261621
// Coerce explicitly specified argument type to contextual type
16271622
// only if both types are valid and do not match.
16281623
if (!hadError && isValidType(ty) && !ty->isEqual(paramType)) {
1629-
assert(!param->isImmutable() || !ty->is<InOutType>());
1630-
param->setType(ty->getInOutObjectType());
1631-
param->setInterfaceType(ty->mapTypeOutOfContext()->getInOutObjectType());
1624+
param->setType(ty);
1625+
param->setInterfaceType(ty->mapTypeOutOfContext());
16321626
}
16331627
}
16341628

1635-
assert(!ty->hasLValueType() && "Bound param type to @lvalue?");
1629+
assert(ty->isMaterializable());
16361630
if (forceMutable) {
16371631
param->setSpecifier(VarDecl::Specifier::InOut);
1638-
} else if (auto *TTy = ty->getAs<TupleType>()) {
1639-
if (param->hasName() && TTy->hasInOutElement()) {
1640-
diagnose(param->getStartLoc(),
1641-
diag::param_type_non_materializable_tuple, ty);
1642-
}
16431632
}
16441633

16451634
// If contextual type is invalid and we have a valid argument type
16461635
// trying to coerce argument to contextual type would mean erasing
16471636
// valuable diagnostic information.
16481637
if (isValidType(ty) || shouldOverwriteParam(param)) {
1649-
assert(!param->isImmutable() || !ty->is<InOutType>());
1650-
param->setType(ty->getInOutObjectType());
1651-
param->setInterfaceType(ty->mapTypeOutOfContext()->getInOutObjectType());
1638+
param->setType(ty);
1639+
param->setInterfaceType(ty->mapTypeOutOfContext());
16521640
}
16531641

16541642
checkTypeModifyingDeclAttributes(param);
@@ -1667,9 +1655,6 @@ bool TypeChecker::coerceParameterListToType(ParameterList *P, ClosureExpr *CE,
16671655
auto getType = [](const AnyFunctionType::Param &param) -> Type {
16681656
auto type = param.getPlainType();
16691657

1670-
if (param.isInOut())
1671-
return InOutType::get(type);
1672-
16731658
if (param.isVariadic())
16741659
return ArraySliceType::get(type);
16751660

lib/Sema/TypeCheckType.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2082,7 +2082,18 @@ bool TypeResolver::resolveASTFunctionTypeParams(
20822082
}
20832083

20842084
ValueOwnership ownership;
2085-
switch (eltTypeRepr->getKind()) {
2085+
2086+
auto *nestedRepr = eltTypeRepr;
2087+
2088+
// Look through parens here; other than parens, specifiers
2089+
// must appear at the top level of a parameter type.
2090+
while (auto *tupleRepr = dyn_cast<TupleTypeRepr>(nestedRepr)) {
2091+
if (!tupleRepr->isParenType())
2092+
break;
2093+
nestedRepr = tupleRepr->getElementType(0);
2094+
}
2095+
2096+
switch (nestedRepr->getKind()) {
20862097
case TypeReprKind::Shared:
20872098
ownership = ValueOwnership::Shared;
20882099
break;
@@ -2098,7 +2109,7 @@ bool TypeResolver::resolveASTFunctionTypeParams(
20982109
}
20992110
ParameterTypeFlags paramFlags =
21002111
ParameterTypeFlags::fromParameterType(ty, variadic, ownership);
2101-
elements.emplace_back(ty->getInOutObjectType(), Identifier(), paramFlags);
2112+
elements.emplace_back(ty, Identifier(), paramFlags);
21022113
}
21032114

21042115
return false;
@@ -2611,16 +2622,12 @@ Type TypeResolver::resolveSpecifierTypeRepr(SpecifierTypeRepr *repr,
26112622
options -= TypeResolutionFlags::TypeAliasUnderlyingType;
26122623
}
26132624

2614-
Type ty = resolveType(repr->getBase(), options);
2615-
if (!ty || ty->hasError()) return ty;
2616-
if (repr->getKind() == TypeReprKind::InOut) return InOutType::get(ty);
2617-
return ty;
2625+
return resolveType(repr->getBase(), options);
26182626
}
26192627

26202628

26212629
Type TypeResolver::resolveArrayType(ArrayTypeRepr *repr,
26222630
TypeResolutionOptions options) {
2623-
// FIXME: diagnose non-materializability of element type!
26242631
Type baseTy = resolveType(repr->getBase(), withoutContext(options));
26252632
if (!baseTy || baseTy->hasError()) return baseTy;
26262633

@@ -2640,7 +2647,6 @@ Type TypeResolver::resolveDictionaryType(DictionaryTypeRepr *repr,
26402647
TypeResolutionOptions options) {
26412648
options = adjustOptionsForGenericArgs(options);
26422649

2643-
// FIXME: diagnose non-materializability of key/value type?
26442650
Type keyTy = resolveType(repr->getKey(), withoutContext(options));
26452651
if (!keyTy || keyTy->hasError()) return keyTy;
26462652

@@ -2761,8 +2767,7 @@ Type TypeResolver::resolveTupleType(TupleTypeRepr *repr,
27612767
Type ty = resolveType(tyR, elementOptions);
27622768
if (!ty || ty->hasError()) return ty;
27632769

2764-
elements.emplace_back(ty->getInOutObjectType(),
2765-
repr->getElementName(i), ParameterTypeFlags());
2770+
elements.emplace_back(ty, repr->getElementName(i), ParameterTypeFlags());
27662771
}
27672772

27682773
// Single-element labeled tuples are not permitted outside of declarations

test/Constraints/closures.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,3 +712,10 @@ extension Node {
712712
})!
713713
}
714714
}
715+
716+
// Make sure we don't allow this anymore
717+
func takesTwo(_: (Int, Int) -> ()) {}
718+
func takesTwoInOut(_: (Int, inout Int) -> ()) {}
719+
720+
takesTwo { _ in } // expected-error {{contextual closure type '(Int, Int) -> ()' expects 2 arguments, but 1 was used in closure body}}
721+
takesTwoInOut { _ in } // expected-error {{contextual closure type '(Int, inout Int) -> ()' expects 2 arguments, but 1 was used in closure body}}

test/decl/func/functions.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,14 @@ func testCurryFixits() {
168168
// Bogus diagnostic talking about a 'var' where there is none
169169
func invalidInOutParam(x: inout XYZ) {}
170170
// expected-error@-1{{use of undeclared type 'XYZ'}}
171+
172+
// Parens around the 'inout'
173+
func parentheticalInout(_ x: ((inout Int))) {}
174+
175+
var value = 0
176+
parentheticalInout(&value)
177+
178+
func parentheticalInout2(_ fn: (((inout Int)), Int) -> ()) {
179+
var value = 0
180+
fn(&value, 0)
181+
}

0 commit comments

Comments
 (0)